题目大意:
给定一个字符串,求每个对应的长度能产生的相同子串的最大个数
这里构建好后缀自动机之后,再将整个字符串从头到尾扫一遍,然后将每个对应的点上的sc值+1
表示从头走到尾的前提下,所能产生的子串能够得到的最大数量为1
然后再去考虑其他子串
每个后缀自动机上的节点上的长度表示的是当前点所能接收的最大长度的后缀
我们只考虑这个最大长度即可,因为其他没考虑的长度,最后都不断通过dp[i] = max(dp[i] , dp[i+1]) 得到即可
拓扑排序后,从尾节点开始不断往前,相当于由子节点不断更新父节点所能得到的值
也就是b[i]->f->sc += b[i]->sc;
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 5 using namespace std; 6 #define N 500010 7 #define M 26 8 9 struct SamNode{ 10 SamNode *son[26] , *f; 11 int l , sc; 12 }*root , *last , sam[N] , *b[N]; 13 14 int cnt , dp[N] , num[N]; 15 char s[N]; 16 17 void init(){ 18 root = last = &sam[cnt=0]; 19 } 20 21 void add(int x) 22 { 23 SamNode *p = &sam[++cnt] , *jp=last; 24 p->l = jp->l+1; 25 last = p; 26 for( ; jp&&!jp->son[x] ; jp=jp->f) jp->son[x]=p; 27 if(!jp) p->f = root; 28 else{ 29 if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; 30 else{ 31 SamNode *r = &sam[++cnt] , *q = jp->son[x]; 32 *r = *q; 33 r->l = jp->l+1; 34 q->f = p->f = r; 35 for( ; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r; 36 } 37 } 38 } 39 40 41 void solve() 42 { 43 init(); 44 int len = strlen(s); 45 for(int i=0 ; i<len ; i++) add(s[i]-'a'); 46 //后面三个for循环相当于进行拓扑排序 47 for(int i=0 ; i<=cnt ; i++) num[sam[i].l]++; 48 for(int i=1 ; i<=len ; i++) num[i]+=num[i-1]; 49 for(int i=0 ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i]; 50 51 SamNode *cur = root; 52 for(int i=0 ; i<len ; i++){ 53 cur = cur->son[s[i]-'a']; 54 cur->sc++; 55 } 56 for(int i=cnt ; i>0 ; i--){ 57 int l = b[i]->l; 58 dp[l] = max(dp[l] , b[i]->sc); 59 b[i]->f->sc += b[i]->sc; 60 } 61 for(int i=len-1 ; i>=1 ; i--) dp[i]=max(dp[i] , dp[i+1]); 62 for(int i=1 ; i<=len ; i++) printf("%d " , dp[i]); 63 } 64 65 int main() 66 { 67 // freopen("a.in" , "r" , stdin); 68 scanf("%s" , s); 69 solve(); 70 return 0; 71 }