您的位置:首页 > Web前端 > JavaScript

[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() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: