題意
給你N個字符串,問你第l個到第r個字符串中有多少個不同前綴。強制在線做,所以沒法用樹狀數組來做。
主席樹
也就是Persistent Segment Tree ,可持久化線段樹。
一般來講線段樹更新之後不會使用歷史版本的線段樹的信息,但是有些問題裏面需要。
可持久化線段樹的做法是,新建一logn個節點,相當於一條鏈,線段樹原本更新時是將這條鏈上的值更新,而主席樹是新建一條鏈,其他部分連接原始的線段樹,這樣既完成了更新操作,又能保留歷史版本。
做法
這個做法同在線查詢一個區間內不同數的個數,T[i]這個樹維護從i開始[i,r]不同數的個數,用一個數組bel維護出現那個前綴最後的位置,從T[i]轉移到T[i-1]就是將當前所有前綴在後面出現過的話就消除影響,並且在當前位置加上當前字符串個數(也就是前綴個數)
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<iostream>
using namespace std;
namespace Trie {
const int SIZE = 100005;
int node[SIZE][26];
int tot, bel[SIZE];
void Insert(string& str) {
int cur = 0;
for(int i = 0; i < str.size(); i++) {
int p = str[i] - 'a';
if(node[cur][p] == 0) {
tot++;
node[cur][p] = tot; //指向新節點
memset(node[tot], 0, sizeof(node[tot]));
}
cur = node[cur][p];
bel[cur] = 0;
}
}
void init() {
tot = 0;
memset(node[0], 0, sizeof(node[0]));
}
// cur <- node[cur][p]
}
namespace PST {
const int MAXN = 100005;
const int M = MAXN * 40;
int tot;
int n;
int T[MAXN],lson[M],rson[M],c[M];
void init(int _n) {
tot = 0;
n = _n;
}
int build(int l,int r)
{
int root = tot++;
c[root] = 0;
if(l != r)
{
int mid = (l+r)>>1;
lson[root] = build(l,mid);
rson[root] = build(mid+1,r);
}
return root;
}
int update(int root,int pos,int val)
{
int newroot = tot++, tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = n;
while(l < r)
{
int mid = (l+r)>>1;
if(pos <= mid)
{
lson[newroot] = tot++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
}
else
{
rson[newroot] = tot++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid+1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
int query(int root,int pos)
{
int ret = 0;
int l = 1, r = n;
while(pos < r)
{
int mid = (l+r)>>1;
if(pos <= mid)
{
r = mid;
root = lson[root];
}
else
{
ret += c[lson[root]];
root = rson[root];
l = mid+1;
}
}
return ret + c[root];
}
}
string s[PST::MAXN];
int main() {
int N;
while(~scanf("%d",&N)) {
PST::init(N);
Trie::init();
for(int i = 1; i <= N; i++) {
cin >> s[i];
Trie::Insert(s[i]);
}
PST::T[N+1] = PST::build(1, N);
for(int i = N; i >= 1; i--) {
int cur = 0;
PST::T[i] = PST::T[i+1];
for(int j = 0; j < s[i].size(); j++) {
int p = s[i][j] - 'a';
cur = Trie::node[cur][p];
if(Trie::bel[cur]) {
//消除前面出現過的前綴的影響
PST::T[i] = PST::update(PST::T[i], Trie::bel[cur], -1);
}
Trie::bel[cur] = i;//通過字典樹分配ID,記錄這個前綴出現的最後一個位置
}
PST::T[i] = PST::update(PST::T[i],i,s[i].size());
}
int Q;
scanf("%d",&Q);
int Z = 0;
while(Q--) {
int l, r;
scanf("%d%d",&l,&r);
l += Z; l %= N;
r += Z; r %= N;
if(l > r) swap(l, r);
Z = PST::query(PST::T[l+1],r+1);
printf("%d\n",Z);
}
}
}