题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4622
这个题目开始是用后缀数组来做的,但是这个题目对后缀数组时间卡的很紧,后来看解题报告说是用后缀自动机搞定的,想想也是CLJ出题怎么会没有后缀
自动机呢
其实这个题目字符串长度不算长2000,但是查询可以达到10000次,如果每次查询都重新建立后缀自动机来计算不同子串的个数的话会超时,但是考虑到后
缀自动机的构造过程的一个在线的,所以我们就考虑能不能先把要查询的区间排序,然后按照左边界排序,左边界相同的按照右边界从小到达排序,这样,
在构造后缀自动机的时候现在的就可以在先前的基础上构造了(一定是后一个的l和前一个的l相等,如果不相等就重新构造),这样我们最多构造两千次
这样复杂度就够了!
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define maxn 2100 struct point { point *father,*next[26]; int val; }po[maxn*3],*root,*tail; int tot,len,ans[110000]; char str[maxn]; struct query { int l,r; int num; }rec[11000]; void add(int c,int l) { point *p=tail,*np=&po[tot++]; np->val=l; while(p&&p->next[c]==NULL) p->next[c]=np,p=p->father; if(p==NULL) np->father=root; else { point *q=p->next[c]; if(p->val+1==q->val) np->father=q; else { point *nq=&po[tot++]; *nq=*q; nq->val=p->val+1; np->father=q->father=nq; while(p&&p->next[c]==q) p->next[c]=nq,p=p->father; } } tail=np; } bool cmp(const query &a,const query &b) { if(a.l != b.l) return a.l < b.l; if(a.r !=b.r) return a.r < b.r; return a.num < b.num; } int start() { memset(po,0,sizeof(po)); len=1,tot=1; root=tail=&po[0]; return 0; } int find_ans()//后缀自动机求不同子串个数 { int i,j,ans=0; for(i=tot-1;i>0;i--) ans+=po[i].val-po[i].father->val; return ans; } int main() { int t,i,j,k,n; scanf("%d",&t); while(t--) { scanf("%s",str+1); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d%d",&rec[i].l,&rec[i].r); rec[i].num=i; } sort(rec+1,rec+1+n,cmp); start(); j=1; for(i=1;i<=n;i++) { if(i==1 || rec[i].l==rec[i-1].l) { for(j;j<=rec[i].r;j++) add(str[j]-'a',len++); } else { start(); for(j=rec[i].l;j<=rec[i].r;j++) add(str[j]-'a',len++); } ans[rec[i].num]=find_ans(); } for(i=1;i<=n;i++) printf("%d\n",ans[i]); } return 0; }