【題解】CF535D Tavas and Malekas

閱讀理解題

題意解釋

給定文本串的長度nn和一個模式串ss,並且已知模式串在文本串中某些出現位置(不一定僅在這些位置出現),求可能的文本串的數量。

分析

首先輸入有可能出現方案爲0的情況(即不合法),於是我們先考慮不合法的判斷。

顯而易見的是當ss出現的結尾位置大於nn時,直接可以判定爲不合法。

然後就是當兩個模式串出現位置有重疊時,假如重疊部分匹配不上,那麼顯然無解。(具體判定方式下文會將)

如果有解,那麼我們只要統計文本串內有多少位置沒有被模式串覆蓋,記爲cntcnt,那麼答案就是26cnt26^{cnt}


具體來考慮如何判斷重疊部分是否匹配。顯然重疊部分是模式串的一個後綴和前綴的重疊,於是我們只要判斷當前位置的後綴和前綴是否匹配。而這個判斷可以用兩種方式實現:KMPKMP和字符串哈希。

KMP

我們發現問題和KMPKMP算法中的nextnext數組定義類似,於是我們可以想到:以模式串末尾爲起始位置,一直跳nextnext指針,途中經過的位置的前綴一定和後綴相等(可以想想nextnext數組的意義)。那麼我們只要把這些位置標記出來,只有在這些位置上重疊,前綴和後綴纔有可能相等。

字符串哈希

涉及到字符串的比較,那麼肯定可以想到字符串哈希。我們可以先預處理模式串的哈希值,然後在重疊的時候判斷前綴和後綴是否相等即可。

代碼

數據有坑,要特判m=0m=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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章