[BZOJ4327]-[JSOI2012]玄武密码-AC自动机
来源:互联网 发布:linux系统入门学习 编辑:程序博客网 时间:2024/05/16 18:57
说在前面
并没有什么好说的…但是要保持格式!
看这个题比较顺眼于是就去切掉了…
题目
BZOJ4327传送门
题目大意
给出一个长串,称之为母串,再给出由很多短串组成的字典。
对于每个短串,需要处理出「该短串的前缀」在「母串」上的最大匹配长度(如果该短串被包含,那么这个长度就是短串长)
短串和长串的字符集均只有「 E 」「 S 」「 W 」「 N 」
输入输出格式
第一行有两个整数,N和M,分别表示母串的长度和短串的个数。
第二行是一个长度为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() ;}
阅读全文
0 0
- 【bzoj4327】【JSOI2012】【玄武密码】【AC自动机】
- [BZOJ4327]JSOI2012 玄武密码(AC自动机)
- [BZOJ4327]-[JSOI2012]玄武密码-AC自动机
- [BZOJ4327][JSOI2012] 玄武密码 后缀自动机
- 【JSOI2012】【BZOJ4327】玄武密码
- [BZOJ4327] JSOI2012玄武密码
- BZOJ4327 玄武密码 (AC自动机)
- bzoj 4327: JSOI2012 玄武密码 (AC自动机)
- BZOJ 4327: JSOI2012 玄武密码
- BZOJ 4327 【JSOI 2012】 玄武密码 AC自动机+dfs
- vijos-1951 玄武密码
- bzoj 1559 密码 AC自动机+状压DP
- bzoj4327
- 玄武
- AC自动机...
- AC自动机
- AC 自动机
- AC自动机
- 安装交叉编译工具arm-linux-gcc命令未找到的问题
- Androidstudio支持中文命名
- 从并发处理谈PHP进程间通信----并发锁表操作
- Redis的常见知识点
- Java POI Excel读取
- [BZOJ4327]-[JSOI2012]玄武密码-AC自动机
- 引用类型约束
- 缓存学习---redis
- Python爬虫框架Scrapy实战
- EJb事物介绍
- 根据android源码提供的系统签名文件制作keystore
- iOS网络请求安全认证(JWT,RSA)
- JDBC获取自增主键,批量操作
- 【Go学习】Go的函数