【题解】CF535D Tavas and Malekas

阅读理解题

题意解释

给定文本串的长度 n n n和一个模式串 s s s,并且已知模式串在文本串中某些出现位置(不一定仅在这些位置出现),求可能的文本串的数量。

分析

首先输入有可能出现方案为0的情况(即不合法),于是我们先考虑不合法的判断。

显而易见的是当 s s s出现的结尾位置大于 n n n时,直接可以判定为不合法。

然后就是当两个模式串出现位置有重叠时,假如重叠部分匹配不上,那么显然无解。(具体判定方式下文会将)

如果有解,那么我们只要统计文本串内有多少位置没有被模式串覆盖,记为 c n t cnt cnt,那么答案就是 2 6 c n t 26^{cnt} 26cnt


具体来考虑如何判断重叠部分是否匹配。显然重叠部分是模式串的一个后缀和前缀的重叠,于是我们只要判断当前位置的后缀和前缀是否匹配。而这个判断可以用两种方式实现: K M P KMP KMP和字符串哈希。

KMP

我们发现问题和 K M P KMP KMP算法中的 n e x t next next数组定义类似,于是我们可以想到:以模式串末尾为起始位置,一直跳 n e x t next next指针,途中经过的位置的前缀一定和后缀相等(可以想想 n e x t next next数组的意义)。那么我们只要把这些位置标记出来,只有在这些位置上重叠,前缀和后缀才有可能相等。

字符串哈希

涉及到字符串的比较,那么肯定可以想到字符串哈希。我们可以先预处理模式串的哈希值,然后在重叠的时候判断前缀和后缀是否相等即可。

代码

数据有坑,要特判 m = 0 m=0 m=0的情况。

KMP

#include <bits/stdc++.h>
#define ll long long
#define P 1000000007
#define MAX 1000005
using namespace std;

ll qpow(ll a, ll n){
    ll res = 1;
    while(n){
        if(n&1) res = res*a % P;
        a = a*a%P;
        n >>= 1;
    }
    return res;
}

char s[MAX];
int n, m, len, Next[MAX], mark[MAX], p[MAX];

void init(){
    int j = 0;
    len = strlen(s+1);
    for(int i = 2; i <= len; i++){
        while(j && s[j+1] != s[i]){
            j = Next[j];
        }
        if(s[j+1] == s[i]) j++;
        Next[i] = j;
    }
    for(int i = len; i; i = Next[i]){
        mark[i] = 1;
    }
}

int main()
{
    cin >> n >> m;
    scanf("%s", s+1);
    init();
    for(int i = 1; i <= m; i++){
        scanf("%d", &p[i]);
    }
    if(!m){
        cout << qpow(26, n) << endl;
        return 0;
    }
    for(int i = 1; i <= m; i++){
        if(p[i]+len-1 > n){
            puts("0");
            return 0;
        }
        if(i > 1 && p[i-1]+len > p[i]){
            int x = p[i-1]+len-p[i];
            if(!mark[x]){
                puts("0");
                return 0;
            }
        }
    }
    int cnt = p[1]-1;
    for(int i = 1; i < m; i++){
        if(p[i]+len < p[i+1]){
            cnt += p[i+1]-p[i]-len;
        }
    }
    cnt += n-p[m]-len+1;
    cout << qpow(26, cnt) << endl;
    
    return 0;
}

字符串哈希

#include <bits/stdc++.h>
#define ll long long
#define P 1000000007
#define MAX 1000005
using namespace std;

ll qpow(ll a, ll n){
    ll res = 1;
    while(n){
        if(n&1) res = res*a % P;
        a = a*a%P;
        n >>= 1;
    }
    return res;
}

const ll b1 = 99979, b2 = 100007;
const ll p = 1e9+7, q = 1e9+9;
ll p1[MAX], sum1[MAX], p2[MAX], sum2[MAX];
char s[MAX];
int n, m, len, a[MAX];

void init(){
    len = strlen(s+1);
    p1[0] = 1;
    for(int i = 1; i <= len; i++){
    	p1[i] = p1[i-1]*b1%p;
    }
    p2[0] = 1;
    for(int i = 1; i <= len; i++){
    	p2[i] = p2[i-1]*b2%q;
    }
    sum1[0] = 0;
    for(int i = 1; i <= len; i++){
        sum1[i] = (sum1[i-1]*b1%p+s[i]-'a')%p;
    }
    sum2[0] = 0;
    for(int i = 1; i <= len; i++){
        sum2[i] = (sum2[i-1]*b2%q+s[i]-'a')%q;
    }
}

int main()
{
    cin >> n >> m;
    scanf("%s", s+1);
    init();
    for(int i = 1; i <= m; i++){
        scanf("%d", &a[i]);
    }
    if(!m){
        cout << qpow(26, n) << endl;
        return 0;
    }
    for(int i = 1; i <= m; i++){
        if(a[i]+len-1 > n){
            puts("0");
            return 0;
        }
        if(i > 1 && a[i-1]+len > a[i]){
            int x = a[i-1]+len-a[i];
            if(sum1[x]!=(sum1[len]%p-sum1[len-x]*p1[x]%p+p)%p || sum2[x]!=(sum2[len]-sum2[len-x]*p2[x]%q+q)%q){
                puts("0");
                return 0;
            }
        }
    }
    
    int cnt = a[1]-1;
    for(int i = 1; i < m; i++){
        if(a[i]+len < a[i+1]){
            cnt += a[i+1]-a[i]-len;
        }
    }
    cnt += n-a[m]-len+1;
    cout << qpow(26, cnt) << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值