2020牛客暑期多校训练营(第二场)A.All with Pairs(hash)

思路:我们先对每个串hash一下,记录所有后缀出现次数。然后枚举每个前缀,看相同的后缀出现了多少次。但是可能会重复计算。因为显然如果有一个形如xy…xy的前缀,匹配到xy…xy的后缀有x个,那么xy前缀匹配到xy后缀的y个必然包含x。但其实这x个不是合法的。。。要把不合法的去掉。就其实是每个位置的next值。。把不合法的去掉即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n;
vector<char>v[N];
unordered_map<unsigned long long,int>vis;
struct hash_table{
  unsigned long long  seed, p;
  unsigned long long Hash[N], tmp[N];
  void set(LL _seed) {
    seed = _seed;
  }
  void work(char *s, int n) {
    tmp[0] = 1;
    Hash[0] = 0;
    for (int i = 1; i <= n; i++){
      tmp[i] = tmp[i - 1] * seed;
      Hash[i] = (Hash[i - 1] * seed + (unsigned long long )(s[i])); //may need change
    }
  }
  unsigned long long get(int l, int r) {
    return ((Hash[r] - Hash[l - 1] * tmp[r - l + 1]));
  }
}g;
int ne[N];
void get_ne(char *p){
  int n=strlen(p);
  ne[0]=-1;
  for(int i=1;i<n;i++){
    int j=ne[i-1];
    while(j>=0 &&p[j+1]!=p[i]){
      j=ne[j];
    }
    if(p[j+1]==p[i])j++;
    ne[i]=j;
  }
}
char a[N*10];
int cnt[N];
int main() {
  ios::sync_with_stdio(false);
  cin>>n;g.set(131);
  for(int i=1;i<=n;i++){
    cin>>a+1;int len=strlen(a+1);
    g.work(a,len);
    for(int j=len;j>=1;j--){
      vis[g.get(j,len)]++;
    }
    for(int j=1;j<=len;j++)v[i].pb(a[j]);
  }
  LL ans=0;
  const int mod=998244353;
  for(int i=1;i<=n;i++){
    int sz=v[i].size();
    for(int j=0;j<sz;j++){
      a[j+1]=v[i][j];
    }
    a[sz+1]='\0';
    get_ne(a+1);
    g.work(a,sz);
    for(int j=1;j<=sz;j++){
      cnt[j]=vis[g.get(1,j)];
      if(ne[j-1]!=-1)cnt[ne[j-1]+1]-=cnt[j];
    }
    for(int j=1;j<=sz;j++){
      ans+=1ll*cnt[j]*j*j%mod;
      ans%=mod;
      cnt[j]=0;
    }
  }
  cout<<ans<<'\n';
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40655981/article/details/107345211