NC19822 我不爱她 kmp+hash

链接:https://ac.nowcoder.com/acm/problem/19822
来源:牛客网

题目描述
终于活成了自己讨厌的样子。

天空仍灿烂,它爱着大海。

你喜欢大海,我爱过你。

世界上充满了巧合。我们把每句话当成一个字符串,我们定义a对b的巧合值为a的最长后缀的长度并且它是恰好是b的前缀,这里的后缀或者前缀包括字符串的本身。
比如字符串“天空仍灿烂她喜欢大海”对“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”的巧合值为5,而字符串“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”对“天空仍灿烂她喜欢大海”的巧合值为2。
现在给出n个字符串由"ab"构成的字符串s1,s2,…,sn,求出对于所有1≤ i,j≤ n,si对sj的巧合值的和。

即求对于每个字符串的前缀,找到对于每个字符串最长匹配的后缀的长度和。

容易想到用hash匹配,可以把复杂度控制在O( ∑ ∣ S ∣ \sum|S| S)。
具体的,读入字符串时首先把每个后缀对应的hash存在map内,统计对应hash的个数,再次遍历前缀时只要加上对应hash个数乘长度即可。

后面就是我觉得这个题目的有趣之处。如上做法会造成重复计算,对于极端样例aaaaaaa明显会计入多次。那么我们就要去重,我第一次想的是容斥去重,发现并不可行,看了题解做法kmp+hash,再回来看,我们发现对于ababa,如果最后一个a跟别人匹配了,那么首先ababa的前缀aba与后缀aba是匹配的,所以对于匹配的那个字符串来说,ababa的后缀必定与前缀也就是我们当前子串的后缀匹配(有点晕,这里建议不看,自己退一退简单多了。),由此,我们可以知道重复计算的串也就是满足kmp匹配规则的前后缀,对于next数组值不为0的,我们只要res[next[j]] -= res[j]即可,res是当前长度对应后缀hash的个数。如果不知道next数组意思的可以,可以理解为字符串的[1~next[i]]与[len-next[i]+1,len]完全匹配。

#include "bits/stdc++.h"

#define int long long
using namespace std;
const int N = 2e6 + 10;
#define Pii pair<int,int>
int h[N];
int p[N];
int nxt[N];
unordered_map<int, int> sf;
void search(string s)
{
    
    
    int k=-1;
    nxt[0]=-1;
    int j=0;
    while(j<s.length())
    {
    
    
        if(k<0 || s[k]==s[j])
        {
    
    
            j++;
            k++;
            nxt[j] = k;
        }
        else {
    
    
            k=nxt[k];
        }

    }
}
int getHash(int l, int r) {
    
    
    return h[r] - h[l - 1] * p[r - l + 1];
}

int res[N];
string str[N];
void solve() {
    
    
    p[0] = 1;
    for (int i = 1; i < N; ++i) {
    
    
        p[i] = p[i - 1] * 13;
    }
    int T;
    cin >> T;
    while (T--) {
    
    
        sf.clear();
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
    
    
            cin >> str[i];
            int len = str[i].length();
            h[0] = 1;
            for (int j = 1; j <= len; ++j) {
    
    
                int id = j - 1;
                h[j] = h[j - 1] * 13 + str[i][id] - 'a' + 1;
            }
            for (int j = 1; j <= len; ++j) {
    
    
                sf[getHash(len - j + 1, len)]++;
            }
        }
        int ans = 0;
        for (int i = 1; i <= n; ++i) {
    
    
            int len = str[i].length();
            h[0] = 1;
            for (int j = 1; j <= len; ++j) {
    
    
                int id = j - 1;
                h[j] = h[j - 1] * 13 + str[i][id] - 'a' + 1;
                res[j] = sf[getHash(1,j)];
            }
            search(str[i]);
            for (int j = 1; j <= len; ++j) {
    
    
                if (nxt[j]) res[nxt[j]] -= res[j];
            }
            for (int j = len; j >= 1; --j) {
    
    
                ans += res[j] * j;
            }
        }
        cout << ans << endl;
    }


}


signed main() {
    
    
//    freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(0);
    solve();
}

猜你喜欢

转载自blog.csdn.net/weixin_45509601/article/details/119677038
今日推荐