zoukankan      html  css  js  c++  java
  • 【bzoj4327】JSOI2012 玄武密码 AC自动机

    题目描述

    在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。 
    很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。 
    经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为N的序列来描述,序列中的元素分别是‘E’,‘S’,‘W’,‘N’,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的M段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。 
    现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢? 

    输入

    第一行有两个整数,N和M,分别表示母串的长度和文字段的个数。 
    第二行是一个长度为N的字符串,所有字符都满足是E,S,W和N中的一个。 
    之后M行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是E,S,W和N中的一个。 

    输出

    输出有M行,对应M段文字。 
    每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。 

    样例输入

    7 3
    SNNSSNS
    NNSS
    NNN
    WSEE

    样例输出

    4
    2
    0


    题解

    AC自动机

    先将所有模式串加入到Trie中,构建fail指针和Trie图。

    开一个bool数组,记录一下每个位置能否到达。

    然后将匹配串在Tries图中跑一遍,跑到某个位置,则将这个位置对应的bool赋为true。

    同时由于fail是当前位置的后缀,所以也应该将fail的bool赋为true。

    这样循环进行,直到某个位置已经为true。

    这种方法能够保证时间复杂度为O(n+ml)。

    最后看某个字符串的哪个位置被赋为true即可。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 10000010
    using namespace std;
    queue<int> q;
    int next[N][4] , fail[N] , len[N] , tot = 1 , pos[100010][110];
    bool vis[N];
    char str[N] , w[N];
    int tra(char ch)
    {
    	return ch == 'E' ? 0 : ch == 'S' ? 1 : ch == 'W' ? 2 : 3;
    }
    void build()
    {
    	int x , i;
    	for(i = 0 ; i < 4 ; i ++ ) next[0][i] = 1;
    	q.push(1);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = 0 ; i < 4 ; i ++ )
    		{
    			if(next[x][i]) fail[next[x][i]] = next[fail[x]][i] , q.push(next[x][i]);
    			else next[x][i] = next[fail[x]][i];
    		}
    	}
    }
    int main()
    {
    	int n , m , i , j , t;
    	scanf("%d%d%s" , &n , &m , str + 1);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		scanf("%s" , w + 1) , len[i] = strlen(w + 1);
    		for(j = t = 1 ; j <= len[i] ; j ++ )
    		{
    			if(!next[t][tra(w[j])]) next[t][tra(w[j])] = ++tot;
    			t = next[t][tra(w[j])] , pos[i][j] = t;
    		}
    	}
    	build();
    	vis[1] = 1 , t = 1;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		t = next[t][tra(str[i])];
    		for(j = t ; !vis[j] ; j = fail[j]) vis[j] = 1;
    	}
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		for(j = len[i] ; j ; j -- ) if(vis[pos[i][j]]) break;
    		printf("%d
    " , j);
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    HDU 3081 Marriage Match II
    HDU 4292 Food
    HDU 4322 Candy
    HDU 4183 Pahom on Water
    POJ 1966 Cable TV Network
    HDU 3605 Escape
    HDU 3338 Kakuro Extension
    HDU 3572 Task Schedule
    HDU 3998 Sequence
    Burning Midnight Oil
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6878381.html
Copyright © 2011-2022 走看看