閱讀理解題
題意解釋
給定文本串的長度和一個模式串,並且已知模式串在文本串中某些出現位置(不一定僅在這些位置出現),求可能的文本串的數量。
分析
首先輸入有可能出現方案爲0的情況(即不合法),於是我們先考慮不合法的判斷。
顯而易見的是當出現的結尾位置大於時,直接可以判定爲不合法。
然後就是當兩個模式串出現位置有重疊時,假如重疊部分匹配不上,那麼顯然無解。(具體判定方式下文會將)
如果有解,那麼我們只要統計文本串內有多少位置沒有被模式串覆蓋,記爲,那麼答案就是。
具體來考慮如何判斷重疊部分是否匹配。顯然重疊部分是模式串的一個後綴和前綴的重疊,於是我們只要判斷當前位置的後綴和前綴是否匹配。而這個判斷可以用兩種方式實現:和字符串哈希。
KMP
我們發現問題和算法中的數組定義類似,於是我們可以想到:以模式串末尾爲起始位置,一直跳指針,途中經過的位置的前綴一定和後綴相等(可以想想數組的意義)。那麼我們只要把這些位置標記出來,只有在這些位置上重疊,前綴和後綴纔有可能相等。
字符串哈希
涉及到字符串的比較,那麼肯定可以想到字符串哈希。我們可以先預處理模式串的哈希值,然後在重疊的時候判斷前綴和後綴是否相等即可。
代碼
數據有坑,要特判的情況。
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;
}