Codeforces Gym 101164 C. Castle (KMP)

题意

初始长为 N 的串 S,以及一个空集合 T。下列有 E 个操作:

  • 1 c :在 S 串尾添加字符 c
  • 2 :将当前串 S 加入到集合 T 中
  • 3 :要求判断 T 中多少个串是当前串 S 的后缀。

解题思路

问题可以转嫁为:每次的询问均可视为当前串有多少满足 特定长度前缀 = 等长后缀 ,其中特定长度由 操作 2 决定。

通过 KMP 的 pre 数组,我们可以知道对于模板串的某个长为 i 的前缀,plen = pre[i] 存储的是该串前缀与后缀的最长匹配长度。此时可知 [1, plen] 子串与 [i-plen+1,i] 子串是完全相同的。

对于判断有多少 T 中的串满足为当前子串 [1, i] 的后缀。若 T 中存在长为 k 的串 [1, k] ,则判断 [1, k] == [i-k+1,i] 等价于 [1, k] == [plen-k+1, plen] ,此判断可连续递归,直到判断是否存在某个时刻的 plen == k 则对其贡献为 1 ,否则为 0 。HINT: 本段均是不考虑复杂度的情况,具体需 操作1, 2, 3 中同步处理可降低复杂度。

代码

#include 
using namespace std;
const int E = 1300000;
int n, e, p, pre[E], cnt[E];
char s[E];
bool inSet[E];
int main()
{
    scanf("%d %d %s", &n, &e, s+1);
    int j = 0, idx = strlen(s+1);
    for(int i=2;i<=n;i++) {
        while(j>0 && s[j+1] != s[i])    j = pre[j];
        if(s[j+1] == s[i])  j++;
        pre[i] = j;
    }

    for(int ica=1;ica<=e;ica++)
    {
        scanf("%d", &p);
        if(p == 1) {
            scanf(" %c", &s[++idx]);
            while(j>0 && s[j+1] != s[idx])  j = pre[j];
            if(s[j+1] == s[idx])    j++;
            pre[idx] = j;
            cnt[idx] = cnt[j];
        } else if(p == 2) {
            if(inSet[idx] == false)
                inSet[idx] = 1,
                cnt[idx]++;
        } else {
            printf("%d\n", cnt[idx]);
        }
    }
}

你可能感兴趣的:(Codeforces,Gym)