题意
给定一个字符串S($ |S| \le 10^6 $)
求S中出现次数不为1的子串长度与出现次数乘积的最大值
思路
用SAM统计子串出现的次数,并记录对应的长度
然后在parent树上遍历一遍就能得出答案
实现的时候注意细节判断
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include <cstdio> #include <algorithm> #include <cstring> using namespace std; long long ans; namespace SAM{ int trans[2000100][26],maxlen[2000100],sz[2000100],link[2000100],Nodecnt,last,barrel[2000100],rank[2000100]; void init(void){ Nodecnt=0; last=0; link[0]=-1; maxlen[0]=0; } void append(int x){ int o = ++Nodecnt; maxlen[o] = maxlen[last] + 1; sz[o] = 1; int p = last; while(p!=-1 && (!trans[p][x]) ){ trans[p][x] = o; p = link[p]; } if(p == -1){ link[o] = 0; last = o; return; } int y = trans[p][x]; if(maxlen[p]+1 == maxlen[y]){ link[o] = y; } else{ int z = ++Nodecnt; for(int j=0;j<26;j++) trans[z][j] = trans[y][j]; maxlen[z] = maxlen[p]+1; while(p!=-1 &&(trans[p][x] == y)){ trans[p][x] = z; p = link[p]; } link[z] = link[y]; link[y] = z; link[o] = z; } last = o; } void CalcSz(void){ int maxsz = 0; for(int i=1;i<=Nodecnt;i++){ barrel[maxlen[i]]++; maxsz = max(maxsz,maxlen[i]); } for(int i=1;i<=maxsz;i++) barrel[i]+=barrel[i-1]; for(int i=1;i<=Nodecnt;i++) rank[barrel[maxlen[i]]--] = i; for(int i=Nodecnt;i>=1;i--){ int tmp = rank[i]; sz[link[tmp]]+=sz[tmp]; } } void CalcAns(void){ for(int i=1;i<=Nodecnt;i++){ if(sz[i]>1){ ans=max(ans,1LL*sz[i]*maxlen[i]); } } } }; char s[1000100];
int main(){ ans = 0; scanf("%s",s+1); int n=strlen(s+1); SAM::init(); for(int i=1;i<=n;i++){ SAM::append(s[i]-'a'); } SAM::CalcSz(); SAM::CalcAns(); printf("%lld\n",ans); return 0; }
|