题目大意:
给定一个长度<2000的串,再给最多可达10000的询问区间,求解区间字符串中的不同子串的个数
这里先考虑求解一整个字符串的所有不同子串的方法
对于后缀自动机来说,我们动态往里添加一个字符,每次添加一个字符进去,我们只考虑那个生成的长度为当前长度的后缀自动机的节点
那么这个节点可接收的字符串的个数就是( p->l - p->f->l ),也就是以当前点为最后节点所能得到的与之前不重复的子串的个数
那么这个问题就很好解决了,共2000个位置,以每一个位置为起点构建一次后缀自动机,一直构建到最后一个字符,过程中不断记录所能得到的子串个数
把这个个数动态保存到f[][]数组中
那么打好了表,最后询问的时候,直接访问这个f[][]即可
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std; 7 #define N 2005 8 #define M 26 9 struct SamNode{ 10 SamNode *son[M] , *f; 11 int l; 12 void init(){ 13 for(int i=0 ; i<M ; i++) son[i] = NULL; 14 f=NULL , l=0; 15 } 16 }sam[N<<1] , *root , *last; 17 18 int cnt , f[N][N] , ret; 19 char s[N] ; 20 21 void init(){ 22 sam[0].init(); 23 root = last = &sam[cnt=0]; 24 } 25 26 void add(int x) 27 { 28 SamNode *p = &sam[++cnt] , *jp=last; 29 p->init(); 30 p->l = jp->l+1; 31 last = p; 32 for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p; 33 if(!jp) p->f = root; 34 else{ 35 if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; 36 else{ 37 SamNode *r = &sam[++cnt] , *q = jp->son[x]; 38 r->init(); 39 *r = *q; r->l = jp->l+1; 40 p->f = q->f = r; 41 for( ; jp&&jp->son[x]==q ; jp=jp->f) jp->son[x] = r; 42 } 43 } 44 ret += p->l-(p->f->l); 45 } 46 47 void solve() 48 { 49 int len = strlen(s); 50 for(int i=0 ; i<len ; i++){ 51 ret = 0; 52 init(); 53 for(int j=i ; j<len ; j++){ 54 add(s[j]-‘a‘); 55 f[i][j] = ret; 56 } 57 } 58 } 59 60 int main() 61 { 62 // freopen("a.in" , "r" , stdin); 63 int T; 64 scanf("%d" , &T); 65 while(T--) 66 { 67 scanf("%s", s); 68 int m , l , r; 69 scanf("%d" , &m); 70 solve(); 71 while(m--){ 72 scanf("%d%d" , &l , &r); 73 printf("%d\n" , f[l-1][r-1]); 74 } 75 } 76 return 0; 77 }
时间: 2024-10-13 22:00:11