ZOJ_3228 Searching the String AC自动机

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 ;
}


你可能感兴趣的:(ACM-杂题)