遇到不能跳转的时候 打标记
/************************************************************** Problem: 4566 User: lxy8584099 Language: C++ Result: Accepted Time:1544 ms Memory:51024 kb ****************************************************************/ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=4e5+50; int ch[N][26],fa[N],len[N],cur[N]; int tot=1,last=1,n,tax[N],a[N],fir[N]; char s[N>>1]; long long ans; void insert(int c) { int x=last,nx=last=++tot; len[nx]=len[x]+1; cur[nx]=1; for(;x&&!ch[x][c];x=fa[x]) ch[x][c]=nx; if(!x) fa[nx]=1; else { int y=ch[x][c]; if(len[y]==len[x]+1) fa[nx]=y; else { int ny=++tot; fa[ny]=fa[y]; len[ny]=len[x]+1; fa[y]=fa[nx]=ny; memcpy(ch[ny],ch[y],sizeof(ch[y])); for(;x&&ch[x][c]==y;x=fa[x]) ch[x][c]=ny; } } } /* fir 标记 可以表示为以该点为结尾的最长后缀会出现多少次 处理endpos集合数 开始匹配 如果能到达节点u 那么他的祖先一定会出现并且未被计算过 但是我们不能直接跳到fa 因为往后还能匹配成功 所以我们在此打一个标记 继续往后匹配 所以该点对答案的贡献就是 (l-len[fa[u]])*cur[u] 因为fa[u]及其祖先会被我们的标记计算 最后反拓扑一边 累加标记 此时的贡献就是 sec[u]*cur[u]*(len[u]-len[f]) */ int main() { scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++) insert(s[i]-'a'); for(int i=1;i<=tot;i++) tax[len[i]]++; for(int i=1;i<=tot;i++) tax[i]+=tax[i-1]; for(int i=1;i<=tot;i++) a[tax[len[i]]--]=i; for(int i=tot;i>=1;i--) cur[fa[a[i]]]+=cur[a[i]]; scanf("%s",s+1); n=strlen(s+1); int u=1,l=0; for(int i=1;i<=n;i++) { int c=s[i]-'a'; while(u&&!ch[u][c]) u=fa[u],l=len[u]; if(!u) u=1,l=0; else ++l,u=ch[u][c]; ans+=1LL*(l-len[fa[u]])*cur[u]; fir[fa[u]]++; } for(int i=tot;i>=1;i--) { int u=a[i],f=fa[u]; fir[f]+=+fir[u]; if(u==1) continue; ans+=1LL*fir[u]*cur[u]*(len[u]-len[f]); } printf("%lld\n",ans); return 0; }

京公网安备 11010502036488号