HDU3518 Boring Counting(字尾自動機)
阿新 • • 發佈:2021-11-11
給出一個字串
詢問有多少個子串滿足
出現至少兩次,且不重疊。
做法:
建出SAM。
對每個節點維護最大的endpos位置L和最小的endpos位置R
然後對每個節點\(i\),取\(min(R-L,len[i])-len[link[i]]\)就是對答案的貢獻。
求和即可。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int nxt[maxn][26],link[maxn],len[maxn],lst=1,tot=1; int L[maxn],R[maxn]; char s[maxn]; vector<int> g[maxn]; void init () { for (int i=0;i<=tot;i++) { for (int j=0;j<26;j++) { nxt[i][j]=0; } link[i]=len[i]=0; L[i]=1000000000; R[i]=0; g[i].clear(); } lst=tot=1; } void sam_extend (char c) { int cur=++tot; len[cur]=len[lst]+1; int p=lst; while (p&&!nxt[p][c-'a']) { nxt[p][c-'a']=cur; p=link[p]; } if (!p) link[cur]=1; else { int q=nxt[p][c-'a']; if (len[p]+1==len[q]) { link[cur]=q; } else { int clone=++tot; len[clone]=len[p]+1; for (int i=0;i<26;i++) nxt[clone][i]=nxt[q][i]; link[clone]=link[q]; while (p&&nxt[p][c-'a']==q) { nxt[p][c-'a']=clone; p=link[p]; } link[q]=link[cur]=clone; } } lst=cur; } void dfs (int u) { for (int v:g[u]) { dfs(v); L[u]=min(L[u],L[v]); R[u]=max(R[u],R[v]); } } int main () { for (int i=0;i<maxn;i++) L[i]=1000000000; while (1) { scanf("%s",s); if (strlen(s)==1&&s[0]=='#') break; for (int i=0;i<strlen(s);i++) { sam_extend(s[i]); L[lst]=R[lst]=i; } for (int i=2;i<=tot;i++) g[link[i]].push_back(i); dfs(1); long long ans=0; for (int i=2;i<=tot;i++) { //printf("%d %d %d %d\n",L[i],R[i],len[i],len[link[i]]); ans+=max(0,min(R[i]-L[i],len[i])-len[link[i]]); } printf("%lld\n",ans); init(); } }