Genes in DNA
题意
给一个文本串和一个模式串,求模式串的所有前缀与后缀的组合在文本串里出现次数和。

当我觉得我懂KMP的时候,KMP给了我一巴掌:不,你不懂!
思路
这题朴素的算法就是把模式串的前缀和后缀组合列出来,跑KMP求出现次数和。显然数据范围不允许,那考虑求贡献的方法,对于文本串中第i个字符,把所有模式串中以这个字符为结尾的前缀 的个数求出来,记为ans1[i],注意,这里的以第i个字符结尾的前缀不是真的 以字符t[i]结尾的前缀,而是类似文本串与模式串kmp匹配时与t[i]匹配,假设某个前缀的长度为len,也就是s[0]…s[len-1]与t[i - len + 1]…t[i]匹配,这样的前缀有ans1[i]个,同样的,把文本串中以第i个字符为开头的后缀ans2[i]求出来,答案就是所有相邻两个位置的前缀数量*后缀数量之和。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
char s1[maxn], s2[maxn];
int nex[maxn], pre[maxn], suf[maxn], ans1[maxn], ans2[maxn];
void getnext(char *s, int d[]) {
    int len = strlen(s);
    nex[0] = -1;
    int i = 0, j = -1;
    while (i < len) {
        if(j == -1 || s[i] == s[j]) {
            i++; j++;
            nex[i] = j;  //这里判断了如果s[i],s[j]相等,表示有相同前缀后缀,然后i++,j++了
            d[i] = d[j] + 1;//所以d[i]表示的是以编号为i-1的字符为结尾的前缀的个数,d[i]=d[j]+1的原因是
        }//以编号i-1的*字符*为结尾的前缀的个数原来就有d[j]个,再加上以i-1为*结尾*的前缀的这个字符串,所以是+1个
        else j = nex[j];
    }
}
void kmp(char *t, char *s, int ans[], int p[]) {//文本串 模式串
    int lent = strlen(t), lens = strlen(s), j = 0, i = 0, num = 0;
    while (i < lent && j < lens) {
        if(j == -1 || t[i] == s[j]) {
            i++; j++;
            ans[i - 1] = p[j] - (j == lens);//t[i],s[j]匹配的时候,就把s串以i字符为结尾的前缀传给t,表示s的前缀中以i-1
        }//这个字符为结尾的前缀个数,j==lens的时候要-1是因为题目要求前缀不能是原串
        else j = nex[j];
        if(j == lens) {
            j = nex[j];//要把t串更新完,所以如果s串匹配完了还要继续跑
        }
    }
}
int main()
{
    int t, Case = 1;
    scanf("%d", &t);
    while (t--) {
        scanf("%s%s", s1, s2);
        getnext(s2, pre);//预处理出s串中前缀的数量pre
        int len2 = strlen(s2), len1 = strlen(s1);
        kmp(s1, s2, ans1, pre);
        //倒过来求后缀数量
        reverse(s1, s1 + len1);
        reverse(s2, s2 + len2);
        getnext(s2, suf);
        kmp(s1, s2, ans2, suf);
        ll ans = 0;
        reverse(ans2, ans2 + len1);//倒过来好算答案
        for (int i = 0; i < len1 - 1; i++)
            ans += (ll)ans1[i] * ans2[i + 1];//答案就是t串中相邻的两个字符为结尾的前缀的个数*为开头的后缀的个数
        printf("Case %d: %lld\n", Case++, ans);
    }
    return 0;
}