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; }

浙公网安备 33010602011771号