2020牛客多校(二) A题 All with Pairs(Hash+kmp)

题意是让我们求取两个字符串前缀和后缀最长的相同子串的长度的平方和

首先,查找两个字符串是否相同可以采用hash的方法,因此可以想到先对所有串的所有后缀求一遍hash

之后枚举每一个串的前缀,观察是否有后缀相同,计算每个前缀相同的后缀数

但是这个并不是答案,原因就是会有重复的被计算进来。

例如题解中的  aba和aba,假如我在枚举第一个串河北第二个串的后缀相同, 这样a和aba都是相同的,但是我们要求的是最长的,因此a是不能作为答案的

因此我们考虑用kmp进行去重,去重原理是,kmp的border具有传递性,也就是如果aba是某个串的border,那么a也是,因此我们枚举到每个串的i位置时,只要将ne[i]的答案减去当前答案就能完成去重

打个比方 字符串为aaaaaa 那么当枚举到 aa,我们可以发现,所有包含aa的一定是包含a的,因此只要减去aa的答案,就是a单独的答案,因为aa也包括aaa的答案等等,这就完成了去重。这就是border的传递性原理。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10;
const int M=1e6+10;
const ull mod=998244353;
const int base=131;
int ne[M];
string s[N];
map<ull,int> m1;
ll res[M];
void Hash(string a){
    int i;
    ull t=0,p=1;
    for(i=a.size()-1;i>=1;i--){
        t+=p*(a[i]-'a'+1);
        p*=base;
        m1[t]++;
    }
}
void get(string a){
    int i,j;
    ne[1]=0;
    for(i=2,j=0;i<a.size();i++){
        while(j&&a[i]!=a[j+1])
            j=ne[j];
        if(a[i]==a[j+1])
            j++;
        ne[i]=j;
    }
}
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    int i;
    for(i=1;i<=n;i++){
        cin>>s[i];
        s[i]=" "+s[i];
        Hash(s[i]);
    }
    ll ans=0;
    for(i=1;i<=n;i++){
        ull t=0;
        int j;
        for(j=1;j<s[i].size();j++){
            t=t*base+s[i][j]-'a'+1;
            res[j]=m1[t];
        }
        get(s[i]);
        for(int l=1;l<s[i].size();l++){
            res[ne[l]]-=res[l];
        }
        for(int l=1;l<s[i].size();l++)
            ans=(ans+res[l]*l%mod*l%mod)%mod;
    }
    cout<<ans<<endl;
}
View Code

 

posted @ 2020-07-16 10:12  朝暮不思  阅读(252)  评论(0编辑  收藏  举报