2020牛客多校第二场A题All with Pairs(Hash+KMP或者ac自动机)

链接:https://ac.nowcoder.com/acm/contest/5667/A

题意:定义了一个函数f(s,t)表示s的前缀和t的后缀的最大匹配是多少,给你n个字符串计算所有两两字符串的f(s,t)的平方和,结果对998244353取余

比如 s= abcb  t=babc  f(s,t)=3  匹配的字符串为(abc)。

题解:统计所有串每一个后缀出现次数用哈希来实现。 再进行遍历所有字符串比较前缀与后缀,统计个数,对于一个串s来说,记cnt[i]为所有串中后缀等于s1~i的数量。

那么答案计算 ans=cnt[i]*i*i。这里的问题是我们按照上面的Hash方法求所有串的每一个后缀出现的次数会进行重复计算 比如aaa,后缀:a,aa,aaa。如果我们当前s串能够匹配aaa的最大长度为3,a,aa这两个串也会被匹配计算进去。这时候运用KMP的next的数组进行跳转去重,也就是容斥.next[]  表示的就是一个固定字符串的最长前缀和最长后缀相同的长度。

cnt[ next [ i ] ] - = cnt [ i ] ; 

正向进行cnt[i]包含后面重复的匹配贡献,所以只用减一次。

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 10;
const int base = 233;
const int mod = 998244353;
int nextt[maxn];
int n;
string s[maxn];
int cnt[maxn * 11];
map<ull, int> mp;

void getNext(string s ){            //预处理模式串  
    int m=s.size();
    nextt[0] = 0;
    nextt[1] = 0;
    for(int i = 1 ; i < m ; i++){
       int j = nextt[i];
       while(j && s[i] != s[j])
          j = nextt[j];
       nextt[i+1] = s[i] == s[j] ? j+1 : 0;
    }
}
void Hash(string &s) {
    ull hash = 0, b = 1;
    for (int i = s.length() - 1; i >= 0; i--, b *= base) {
        hash += b * (s[i] - 'a' + 1);
        mp[hash]++;
        //cout<<mp[hash]<<" "<<hash<<" "<<endl;
    }
}
int main() {

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> s[i];
        Hash(s[i]);
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        getNext(s[i]);
        ull hash = 0;
        for (int j = 0; j < s[i].length(); j++) {
            hash = hash * base + s[i][j] - 'a' + 1;
            //cout<<hash<<endl;
            cnt[j+1]=mp[hash];
            cnt[nextt[j+1]] -= cnt[j + 1] ;
        }
        for (int j = 1; j <= s[i].length(); j++)
            ans = (ans + 1ll * cnt[j] * j % mod * j % mod) % mod;
    }
    printf("%lld\n", ans);


    return 0;
}

 

posted @ 2020-08-02 14:57  杰瑞与汤姆  阅读(192)  评论(0)    收藏  举报