http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3228
题意:
给你一个长度为N的字符串和M个长度小于7的substr,分别求在允许重叠和不允许
重叠的情况下,每个substr最多出现多少次。
思路:
这是一个多模式匹配的问题,所以要用AC自动机。在允许重叠的情况下,用AC
自动机求解方法很容易可以想到,但是不允许重叠的情况似乎就比较难处理了。首先
我们可以想到的一种方法是dp,用dp[i][j] 表示长度为i时,第j个串的出现次数,但是
这样的话时间和空间复杂度都要O(N*LEN),显然两者都无法满足,为了降低复杂度,
我们可以用类似于重叠的方式去想:允许重叠就是因为用了AC自动机中的fail指针,
从而可以使复杂度降低到O(LEN+N),因此我们这里也可以使用AC自动机的这个性
质,只是和允许重叠不同的是,我们每次加1是有条件的,那就是上次匹配到的位置
和这次匹配到的位置的距离要大于该substr的长度,为了达到这一点, 我们可以在trie
树的每个结点中增加一个pos域,该域用来记录上次匹配到的时候的位置。
其实这样处理这个问题还有一个地方是没有证明的,那就是为什么遇到一次substr
就匹配最终得到的答案就是匹配的最多次数,下面给出一个不是很严格的证明:假设
现在我们需要求的区间是[ i, j ] ,第一个匹配到的位置是pos1 ( i < pos1 < j) ,第二个匹
配到的位置是pos2,其中pos1 < pos2,现在我们假设当匹配pos2的时候比匹配pos1的
时候更优,也就是说最终先匹配pos2的可以匹配的次数res2要大于先匹配pos1的res1。
因为两个字符串是一样的,所以第一次匹配的结束位置因为是end1 < end2 ,由这个条
件我们可以看出在先匹配pos2后能找到的匹配点,在先匹配pos1的时候都能匹配到,由
此说明先匹配pos1,不会得出一个比最优解更差的解,由此说明上述的求解过程可以得
出一个最优解。
代码:
#include
#include
#include
const int MAXN = 100010 ;
char str[MAXN] ;
bool one[MAXN] ;
char ch[MAXN][7] ;
int *ans[MAXN] ;
int N ;
struct Node{
int fail ;
int num[2] ;
int len ;
int son[26] ;
int pos ;
void init(){
fail = -1 ;
num[0] = num[1] = 0;
pos = -1 ;
memset(son , -1, sizeof(son));
}
}p[MAXN*7] ;
int Root, cnt ;
void build(int n,int f){
int loc = Root, idx ,len = strlen(ch[n]) ;
for(int i=0;i que ;
void build_ac(){
while(!que.empty()) que.pop() ;
p[Root].fail = -1 ;
que.push(Root) ;
while(!que.empty()){
int u = que.front() ; que.pop() ;
for(int i=0;i<26;i++){
if( p[u].son[i] == -1 ) continue ;
else{
int v = p[u].son[i] ;
if( u == Root ){
p[v].fail = Root ;
}
else{
int temp = p[u].fail ;
while( temp != -1 ){
if( p[temp].son[i] != -1 ){
p[v].fail = p[temp].son[i] ;
break ;
}
temp = p[temp].fail ;
}
if(temp == -1)
p[v].fail = Root ;
}
}
que.push( p[u].son[i] ) ;
}
}
}
void work(){
int loc = Root ;
int len = strlen(str) ;
for(int i=0;i=p[temp].len){
p[temp].num[1] ++ ;
p[temp].pos = i ;
}
}
temp = p[temp].fail ;
}
}
}
int main(){
int a ; Root = 0 ;
int cas = 0 ;
while(scanf("%s",str) == 1){
++cas ;
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%d %s",&a,ch[i]);
one[i] = (a==1?1:0) ;
}
p[Root].init() ; cnt = 0 ;
for(int i=1;i<=N;i++){
build( i ,one[i] ) ;
}
build_ac();
work() ;
printf("Case %d\n",cas);
for(int i=1;i<=N;i++){
printf("%d\n",*ans[i]);
}
printf("\n") ;
}
return 0 ;
}