1. 程式人生 > 實用技巧 >2020牛客多校(二) A題 All with Pairs(Hash+kmp)

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