Gym101194F Mr. Panda and Fantastic Beasts(广义SAM+最短路)
发布日期:2021-06-30 10:32:31
浏览次数:2
分类:技术文章
本文共 6864 字,大约阅读时间需要 22 分钟。
①.标记法
直接对 n n n个串建立广义 S A M SAM SAM
如果是第一个串插入产生的节点标记为 0 0 0,其余串插入的节点标记为 1 1 1
然后由于祖先节点都是自己的后缀,如果自己属于第一个串,那么祖先也是第一个串
所以基数排序一遍,把标记传递给祖先节点
于是,每个点是否独立属于第一个串我们就知道了。
我们从自动机的根节点开始 b f s bfs bfs,保存到达每个点的最短路
第一个搜到的合法节点(只在第一个串出现),就是我们的答案,途经的转移边就是子串
正版广义SAM(特判重复节点版本)
#includeusing namespace std;const int maxn = 1e6+10;int casenum;int zi[maxn][27],fa[maxn],l[maxn],id=1,las=1,f[maxn];char a[maxn];void insert(int c,int ok){ int p = las; if( zi[p][c] )//重复节点,所以我们需要把q,nq都染色,因为这次并没有新建节点!! { int q = zi[p][c]; if( l[q]==l[p]+1 ) las = q,f[q] = ok; else { int nq = ++id; las = nq; memcpy( zi[nq],zi[q],sizeof zi[nq] ); fa[nq] = fa[q], l[nq] = l[p]+1; f[nq] = f[q] | ok; fa[q] = nq; for( ; p&&zi[p][c]==q;p=fa[p] ) zi[p][c] = nq; } } else { int np = ++id; las = id; l[np] = l[p]+1; f[np] = ok; for( ; p&&zi[p][c]==0 ;p=fa[p] ) zi[p][c] = np; if( p==0 ) fa[np] = 1; else { int q = zi[p][c]; if( l[q]==l[p]+1 ) fa[np] = q; else { int nq = ++id; memcpy( zi[nq],zi[q],sizeof zi[nq] ); l[nq] = l[p]+1, fa[nq] = fa[q]; f[nq] = f[q]; fa[q] = fa[np] = nq; for( ; p&&zi[p][c]==q; p=fa[p] ) zi[p][c] = nq; } } }}int c[maxn],rk[maxn];void chiken_sort(){ for(int i=1;i<=id;i++) c[l[i]]++; for(int i=1;i<=id;i++) c[i] += c[i-1]; for(int i=1;i<=id;i++) rk[c[l[i]]--] = i; for(int i=id;i>=1;i--) { int u = rk[i]; f[fa[u]] |= f[u]; }}void build(int ok){ las = 1; scanf("%s",a+1 ); int n = strlen( a+1 ); for(int i=1;i<=n;i++) insert( a[i]-'a',ok );}int pre[maxn],vis[maxn];char ans[maxn];void dfs(int ed){ if( ed==1 ) return; dfs( pre[ed] ); printf("%c",ans[ed] );}void solve(){ queue q; q.push( 1 ); int ed = 0; for(int i=1;i<=id;i++) vis[i] = 0; while( !q.empty() ) { int u = q.front(); q.pop(); if( f[u]==0 ) { ed = u; break; } for(int i=0;i<=25;i++) { int v = zi[u][i]; if( v==0 || vis[v] ) continue; vis[v] = 1; ans[v] = 'a'+i; pre[v] = u; q.push( v ); } } printf("Case #%d: ",++casenum); if( ed==0 ) puts("Impossible"); else { dfs(ed); cout << '\n'; }}int main(){ int t; cin >> t; while( t-- ) { int n; cin >> n; build( 0 ); for(int i=2;i<=n;i++) build( 1 ); chiken_sort(); solve(); for(int i=1;i<=id;i++) { memset( zi[i],0,sizeof zi[i] ); f[i] = fa[i] = l[i] = c[i] = rk[i] = 0; } las = id = 1; }}
普通广义 S A M SAM SAM(无特判)
#includeusing namespace std;const int maxn = 1e6+10;int casenum;int zi[maxn][27],fa[maxn],l[maxn],id=1,las=1,f[maxn];char a[maxn];void insert(int c,int ok){ int p = las,np = ++id; las = id; l[np] = l[p]+1; f[np] = ok; for( ; p&&zi[p][c]==0 ;p=fa[p] ) zi[p][c] = np; if( p==0 ) fa[np] = 1; else { int q = zi[p][c]; if( l[q]==l[p]+1 ) fa[np] = q; else { int nq = ++id; memcpy( zi[nq],zi[q],sizeof zi[nq] ); l[nq] = l[p]+1, fa[nq] = fa[q]; f[nq] = f[q]; fa[q] = fa[np] = nq; for( ; p&&zi[p][c]==q; p=fa[p] ) zi[p][c] = nq; } }}vector vec[maxn];void dfs_fail(int u){ for( auto v:vec[u] ) { dfs_fail(v); f[u] |= f[v]; }}void build(int ok){ las = 1; scanf("%s",a+1 ); int n = strlen( a+1 ); for(int i=1;i<=n;i++) insert( a[i]-'a',ok );}int pre[maxn],vis[maxn];char ans[maxn];void dfs(int ed){ if( ed==1 ) return; dfs( pre[ed] ); printf("%c",ans[ed] );}void solve(){ queue q; q.push( 1 ); int ed = 0; for(int i=1;i<=id;i++) vis[i] = 0; while( !q.empty() ) { int u = q.front(); q.pop(); if( f[u]==0 ) { ed = u; break; } for(int i=0;i<=25;i++) { int v = zi[u][i]; if( v==0 || vis[v] ) continue; vis[v] = 1; ans[v] = 'a'+i; pre[v] = u; q.push( v ); } } printf("Case #%d: ",++casenum); if( ed==0 ) puts("Impossible"); else { dfs(ed); cout << '\n'; }}int main(){ int t; cin >> t; while( t-- ) { int n; cin >> n; build( 0 ); for(int i=2;i<=n;i++) build( 1 ); for(int i=2;i<=id;i++) vec[fa[i]].push_back( i ); dfs_fail(1); solve(); for(int i=1;i<=id;i++) { vec[i].clear(); memset( zi[i],0,sizeof zi[i] ); f[i] = fa[i] = l[i] = 0; } las = id = 1; }}
②.动态匹配法
我们只把后 n − 1 n-1 n−1个串建立广义 S A M SAM SAM
拿第一个串在自动机上跑即可
当我们插入完 i i i节点后发现失配了,之前的匹配节点是 x x x,匹配长度是 l l l
我们就需要往上跳 f a fa fa去匹配,如果仍然没有转移边就重复跳 f a fa fa
向上跳跃的过程中,我们经过了若干个没有转移边的节点
记这些节点为 p p p,其中最短的子串为 l [ f a p ] + 1 l[fa_p]+1 l[fap]+1,那么最短 l [ f a p ] + 2 l[fa_p]+2 l[fap]+2就会适配(加上当前的 a i a_i ai字符)
最后更新一下就好了
#define others#ifdef poj#include#include #include #include #include #include #include #include
转载地址:https://issue-is-vegetable.blog.csdn.net/article/details/116015415 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
很好
[***.229.124.182]2024年04月16日 10时36分40秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
怎么申请支持微信登录的企业电子邮箱
2019-04-30
163个人电子邮箱如何申请,邮箱账号都有什么格式你知道吗
2019-04-30
微信企业邮箱登录人口,企业邮箱登陆登录入口
2019-04-30
什么是企业邮箱,如何申请企业邮箱,企业邮箱一年多少钱?
2019-04-30
外贸邮箱选择,外贸企业邮箱注册,海外邮箱申请
2019-04-30
哪家企业邮箱好?免费企业邮箱来一个?邮件服务器谁家好用?
2019-04-30
企业邮箱大全,企业邮箱查询,最大的邮箱是哪个?
2019-04-30
企业邮箱怎么注册流程?企业邮箱域名怎么注册?
2019-04-30
企业电子信箱,电子邮箱格式,企业邮箱怎么注册?
2019-04-30
如何申请企业邮箱注册,如何购买邮箱?
2019-04-30
购买企业邮箱,哪个邮箱最好用?邮件撤回怎么操作?
2019-04-30
电子邮箱是什么?如何申请电子邮箱,申请电子邮箱好处
2019-04-30
如何注册海外邮箱?如何进行邮箱注册163,这些技巧交给你
2019-04-30
企业邮件注册,手机怎么注册邮箱?
2019-04-30
【转载】舍弃 Python+C,Salesforce 将企业级软件全面迁移到 Go 语言
2019-04-30
ConnectorStartFailedException
2019-04-30
Windows上配置Gradle环境
2019-04-30