[BZOJ4327]-[JSOI2012]玄武密码-AC自动机
2017-12-12 11:51
417 查看
说在前面
并没有什么好说的…但是要保持格式!看这个题比较顺眼于是就去切掉了…
题目
BZOJ4327传送门题目大意
给出一个长串,称之为母串,再给出由很多短串组成的字典。对于每个短串,需要处理出「该短串的前缀」在「母串」上的最大匹配长度(如果该短串被包含,那么这个长度就是短串长)
短串和长串的字符集均只有「 E 」「 S 」「 W 」「 N 」
输入输出格式
第一行有两个整数,N和M,分别表示母串的长度和短串的个数。 N≤1e7,M≤1e5第二行是一个长度为N的字符串,表示母串。
之后M行,每行有一个字符串,表示短串,短串长小于等于100。
解法
很简单的AC自动机题=w=求一个短串是否在一个长串中出现过,可以把短串的fail处理出来跑KMP。求最大匹配前缀长度的话,在KMP里随便加个变量记录一下就ok
然而数据规模吓死人,显然是不可能一个一个KMP的= =
那么显然需要用到AC自动机(废话,字典都给出来了),建立出fail指针之后,把母串拿到AC自动机上跑一遍。每到一个节点,就给这个节点打上vis标记。凡是AC自动机上被vis过的节点,就说明这个节点所代表的前缀被包含了。同时如果一个点被vis,那么它的fail也应该有vis标记。因此跑完之后,再自底向上更新vis标记。
最后把字典里每一个短串在trie上查一下最深的被vis过的点就ok了
下面是自带大常数的代码
/************************************************************** Problem: 4327 User: Izumihanako Language: C++ Result: Accepted Time:3428 ms Memory:188740 kb ****************************************************************/ #include <queue> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; int N , M ; char ss[100005][105] , a[10000005] ; struct Node{ bool vis ; char c ; Node *ch[5] , *fail ; }*root ; void newNode( Node *&nd ){ nd = new Node() ; for( int i = 0 ; i < 5 ; i ++ ) nd->ch[i] = NULL ; nd->fail = NULL ; nd->vis = false ; } void ext( char *ts ,int len ){ for( int i = 0 ; i < len ; i ++ ) switch( ts[i] ){ case 'E' : ts[i] = 1 ; break ; case 'S' : ts[i] = 2 ; break ; case 'W' : ts[i] = 3 ; break ; case 'N' : ts[i] = 4 ; break ; } } void Insert( char *ts ){ int len = strlen( ts ) ; Node *nd = root ; for( int i = 0 ; i < len ; i ++ ){ int nxt = ts[i] ; if( !nd->ch[nxt] ){ newNode( nd->ch[nxt] ) ; //printf( "address :%d\n" , nd->ch[nxt] ) ; nd->ch[nxt]->c = ts[i] ; } nd = nd->ch[nxt] ; } } queue<Node*> que ; void getFail(){ que.push( root ) ; while( !que.empty() ){ Node *u = que.front() ; que.pop() ; for( int i = 1 ; i < 5 ; i ++ ){ if( !u->ch[i] ) continue ; Node *p = u->fail , *v = u->ch[i] ; while( p && !p->ch[i] ) p = p->fail ; v->fail = ( p ? p->ch[i] : root ) ; que.push( v ) ; } } root->fail = root ; } void Run(){ Node *nd = root ; root->vis = true ; for( int i = 0 ; i < N ; i ++ ){ int nxt = a[i] ; //printf( "adress(%d-[%d]) nxt :%d\n" , nd , nd->c , nxt ) ; while( nd != root && !nd->ch[nxt] ){ nd = nd->fail ; nd->vis = true ; } if( nd->ch[nxt] ){ nd = nd->ch[nxt] ; nd->vis = true ; } } } Node *sta[40000005] ; void bfs(){ int pt = 0 , topp = 0 ; sta[++topp] = root ; while( pt != topp ){ pt ++ ; Node *u = sta[pt] ; for( int i = 1 ; i < 5 ; i ++ ) if( u->ch[i] ) sta[++topp] = u->ch[i] ; } for( int i = topp ; i ; i -- ){ Node *u = sta[i] ; if( u->vis ) u->fail->vis = true ; } } int Query( char *ts ){ int len = strlen( ts ) , rt = 0 ; Node *nd = root ; for( int i = 0 ; i < len ; i ++ ){ int nxt = ts[i] ; nd = nd->ch[nxt] ; if( !nd->vis ) return rt ; rt ++ ; } return rt ; } void solve(){ getFail() ; Run() ; bfs() ; for( int i = 1 ; i <= M ; i ++ ) printf( "%d\n" , Query( ss[i] ) ) ; } int main(){ scanf( "%d%d" , &N , &M ) ; scanf( "%s" , a ) ; ext( a , N ) ; newNode( root ) ; root->c = 0 ; for( int i = 1 ; i <= M ; i ++ ){ scanf( "%s" , ss[i] ) ; ext( ss[i] , strlen( ss[i] ) ) ; Insert( ss[i] ) ; } solve() ; }
相关文章推荐
- [BZOJ]4327 [JSOI2012] 玄武密码 AC自动机
- BZOJ[4327]JSOI2012 玄武密码 AC自动机
- 【BZOJ4327】JSOI2012 玄武密码 AC自动机
- 【bzoj4327】【JSOI2012】【玄武密码】【AC自动机】
- BZOJ-4327:JSOI2012 玄武密码(AC自动机模板题)
- BZOJ4327 : JSOI2012 玄武密码
- BZOJ_4327_JSOI2012 玄武密码_AC自动机
- BZOJ 4327 JSOI2012 玄武密码(后缀自动机)
- BZOJ 4327: JSOI2012 玄武密码
- [BZOJ4327] JSOI2012玄武密码
- BZOJ 4327 【JSOI 2012】 玄武密码 AC自动机+dfs
- 【JSOI2012】【BZOJ4327】玄武密码
- bzoj 4327: JSOI2012 玄武密码 (AC自动机)
- [BZOJ4327]JSOI2012 玄武密码(AC自动机)
- 【BZOJ4327】【JSOI2012】玄武密码
- 4327: JSOI2012 玄武密码[SAM]
- BZOJ4327 JSOI2012玄武密码(AC自动机)
- [JSOI2012]玄武密码
- BZOJ1559 [JSOI2009]密码 【AC自动机 + 状压dp】
- BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )