CF432D Prefixes and Suffixes

CF432D Prefixes and Suffixes

题意

给你一个长度为n的长字符串,“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在长字符串中出现的次数

分析

求出nex数组 , 在求出每个前缀出现的次数 , 从nex[n] 往下走就行了

其实这道题是 , KMP 求每个前缀出现次数的模板题

求前缀出现次数的写法

    for(int i = 1 ; i <= n ; ++i) num[i]++;
    for(int i = n ; i >= 1 ; --i) num[nex[i]] += num[i];

这个的意思是 , 首先长度为 i 的前缀有大自己 , 也就是 1

在考虑 长的为 i 的前缀可以把它的次数转移给谁呢?前缀本身已经计算过了, 接下来只要计算不是前缀本身的 , nex[i] 是他的最长公共前后缀,也就是有一个以 i 为右端点, 长为 nex[i] 的子串 和前缀相同 , 所以 i 出现多少次 , nex[i] 的前缀也应该加上他出现的次数

    for(int i = 1 ; i <= n ; ++i) num[nex[i]]++;
    for(int i = n ; i >= 1 ; --i) num[nex[i]] += num[i];
    for(int i = 1 ; i <= n ; ++i) num[i]++;

这个的意思是 , 先刨除他本身 ,再在最后加上;

个人推荐第一种好理解 , 还短

上本题代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5+100;
int n;
int nex[N] , num[N] , cnt[N];
char c[N];
int main()
{
    scanf("%s",c+1); n = strlen(c+1);
    for(int i = 2 , k = 0 ; i <= n ; ++i)
    {
        while(k && c[i] != c[k + 1]) k = nex[k];
        if(c[i] == c[k + 1]) k++; nex[i] = k;
    }
    int tot = 0 , k = nex[n];
    while(k) cnt[++tot] = k , k = nex[k];
    printf("%d\n" , tot + 1);
//  for(int i = 1 ; i <= n ; ++i) num[i]++;
//  for(int i = n ; i >= 1 ; --i) num[nex[i]] += num[i];
    
    for(int i = 1 ; i <= n ; ++i) num[nex[i]]++;
    for(int i = n ; i >= 1 ; --i) num[nex[i]] += num[i];
    for(int i = 1 ; i <= n ; ++i) num[i]++;
    for(int i = tot ; i >= 1 ; --i)
        printf("%d %d\n" , cnt[i] , num[cnt[i]]);
    printf("%d 1\n" , n);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/R-Q-R-Q/p/12141659.html