BZOJ4566: [Haoi2016]找相同字符

题解: 我们考虑对第一个串建SAM  另一个串在第一个上跑 我们预处理出第一个串在parent树中以当前字符结尾产生的价值且下传到儿子节点 等于每个节点维护了 他在parent 树中到根这条链的所有子串的价值和 那么我们在来计算合并价值 第二串跑的时候 找到位置匹配的最远位置 而且我们必然可以得到他的祖先节点一定也会被匹配到 所以加上预处理的价值以及当前匹配的最远距离的价值 累积答案即可

/**************************************************************
    Problem: 4566
    User: c20161007
    Language: C++
    Result: Accepted
    Time:1800 ms
    Memory:57152 kb
****************************************************************/
 
#include <bits/stdc++.h>
const int MAXN=4e5+10;
#define ll long long
using namespace std;
#define link(x) for(edge *j=h[x];j;j=j->next)
int dis[MAXN],fa[MAXN],ch[MAXN][26],size[MAXN],sz[MAXN];
int cnt,cur,rt;
char s1[MAXN],s2[MAXN];
bool vis[MAXN];
struct edge{int t;edge*next;}e[MAXN],*h[MAXN],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
void built(int x){
    int last=cur;cur=++cnt;dis[cur]=dis[last]+1;int p=last;size[cur]=1;
    for(;p&&!ch[p][x];p=fa[p])ch[p][x]=cur;
    if(!p)fa[cur]=rt;
    else{
        int q=ch[p][x];
        if(dis[p]+1==dis[q])fa[cur]=q;
        else{
            int nt=++cnt;dis[nt]=dis[p]+1;
            memcpy(ch[nt],ch[q],sizeof(ch[q]));
            fa[nt]=fa[q];fa[q]=fa[cur]=nt;
            for(;ch[p][x]==q;p=fa[p])ch[p][x]=nt;
        }
    }
}
int sa[MAXN],txt[MAXN];
void dfs(int x,int pre){
    link(x){
        if(j->t!=pre){dfs(j->t,x);size[x]+=size[j->t];}
    }
}
void slove(){
    scanf("%s",s2+1);int t=strlen(s2+1);int temp=rt;
    ll ans=0;int len=0;
    for(int i=1;i<=t;i++){
        int t=s2[i]-'a';
        if(ch[temp][t])len++,temp=ch[temp][t];
        else{
            int p=temp;
            for(;p&&!ch[p][t];p=fa[p]);
            if(!p)temp=rt,len=0;
            else temp=ch[p][t],len=dis[p]+1;
        }
        if(temp!=rt)ans+=sz[fa[temp]]+1LL*size[temp]*max(0,len-dis[fa[temp]]);
    }
    printf("%lld
",ans);
    return ;
}
int main(){
    scanf("%s",s1+1);int len=strlen(s1+1);
    rt=cnt=cur=1;
    for(int i=1;i<=len;i++)built(s1[i]-'a');
    for(int i=1;i<=cnt;i++)add(fa[i],i);
    dfs(rt,0);
    for(int i=1;i<=cnt;i++)txt[dis[i]]++;
    for(int i=1;i<=cnt;i++)txt[i]+=txt[i-1];
    for(int i=cnt;i>=1;i--)sa[txt[dis[i]]--]=i;
    for(int i=1;i<=cnt;i++)sz[sa[i]]=sz[fa[sa[i]]]+1LL*(dis[sa[i]]-dis[fa[sa[i]]])*size[sa[i]];
    //for(int i=2;i<=cnt;i++)sz[i]=sz[fa[i]]+1LL*(dis[i]-dis[fa[i]])*size[i];
    slove();
}

  

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1109  Solved: 646
[Submit][Status][Discuss]

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。

Input

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

Output

输出一个整数表示答案

Sample Input

aabb
bbaa

Sample Output

10
原文地址:https://www.cnblogs.com/wang9897/p/9637199.html