帶可選字符的多字符串匹配
Problem Description
有一個文本串,它的長度爲m(1≤m≤2000000),現在想找出其中所有的符合特定模式的子串位置。
符合特定模式是指,該子串的長度爲n(1≤n≤500),並且第i個字符需要在給定的字符集合Si中。
因此,描述這一特定模式,共需要S1,S2,…,Sn這n個字符集合。每個集合的大小都在1∼62之間,其中的字符只爲數字或大小寫字母。
Input
第一行爲一個字符串,表示待匹配的文本串。注意文本串中可能含有數字和大小寫字母之外的字符。
第二行爲一個整數n。
以下n行,分別描述n個字符集合。每行開始是一個1∼62之間的整數,隨後有一個空格,接下來有一個字符串表示對應字符集合的內容。整數表示字符集合的大小,因此它也就是字符串的長度。輸入保證字符串中的字符只爲數字或大小寫字母且沒有重複。(注:本題有多組測試數據)
Output
每當從某個位置開頭的,長度爲n的子串符合輸入的模式,就輸出一行,其中包含一個整數,爲它在文本串的起始位置。位置編號從1開始。
如果文本串沒有任何位置符合輸入模式,則最後輸出一個字符串”NULL”,佔一行。
Sample Input
aaaabacabcabd
3
3 abc
2 bc
3 abc
Sample Output
4
6
8
9
這應該是一道shift-and的模板題了。
注意到每個位置是一個字符集對shift-and並沒有影響,仍然只需要處理出文本串中每個字符在匹配串中出現位置,然後直接shift-and即可。
關於shift-and算法,用了一個數組 , 表示匹配串以 結尾的前綴是否是文本串以 結尾的前綴的後綴,簡單來說就是模式串以 結尾能否匹配當前位置。
考慮轉移, 當且僅當 && ,令 表示字符i在串 中出現位置的狀壓。那麼有 & ,利用bitset實現,可做到 ,當匹配串較短時可認爲是線性。
代碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<bitset>
#define N 2000005
using namespace std;
char s[N],ss[N];
bitset<501>F,B[63];
int id(char t)
{
if(t>='0'&&t<='9')return t-47;
if(t>='A'&&t<='Z')return 11+t-'A';
if(t>='a'&&t<='z')return 37+t-'a';
return 0;
}
int main()
{
int i,j,k,n;char c;bool f;
while(gets(s+1))
{
int l=strlen(s+1);
F.reset();f=0;
for(i=0;i<63;i++)B[i].reset();
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&k);
scanf("%s",&ss[1]);
for(j=1;j<=k;j++)B[id(ss[j])][i]=1;
}
for(i=1;i<=l;i++)
{
F<<=1;F[1]=1;
F&=B[id(s[i])];
if(F[n]==1)printf("%d\n",i-n+1),f=1;
}
if(!f)puts("NULL");
c=getchar();
}
}