AC自动机练习题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zyszlb2003/article/details/99826842

caioj1465
【题意】
给出有一个 L C L*C 的字符地图,地图的行与列都从 0 0 开始编号
然后给出一些字符串,求出这些字符串在字符地图上的位置。(数据保证有唯一解)
输出字符串第一个字母的坐标和字符串的方向
字符串的方向是指字符串的走向
A表示正北,B表示东北,C表示正东,D表示东南,E表示正南,F表示西南,G表示正西,H表示西北
且保证字符串的方向是固定的
【输入格式】
第一行输入L,C,W(0<L,C,W<=1000)
L表示行数,C表示列数,W表示字符串的数量
然后输入 L C L*C 的字符矩阵
最后输入W行字符串
【输出格式】
输出W行,每行对应第i个字符串第一个字母的坐标和字符串的方向
【样例输入】
20 20 10
QWSPILAATIRAGRAMYKEI
AGTRCLQAXLPOIJLFVBUQ
TQTKAZXVMRWALEMAPKCW
LIEACNKAZXKPOTPIZCEO
FGKLSTCBTROPICALBLBC
JEWHJEEWSMLPOEKORORA
LUPQWRNJOAAGJKMUSJAE
KRQEIOLOAOQPRTVILCBZ
QOPUCAJSPPOUTMTSLPSF
LPOUYTRFGMMLKIUISXSW
WAHCPOIYTGAKLMNAHBVA
EIAKHPLBGSMCLOGNGJML
LDTIKENVCSWQAZUAOEAL
HOPLPGEJKMNUTIIORMNC
LOIUFTGSQACAXMOPBEIO
QOASDHOPEPNBUYUYOBXB
IONIAELOJHSWASMOUTRK
HPOIYTJPLNAQWDRIBITG
LPOINUYMRTEMPTMLMNBO
PAFCOPLHAVAIANALBPFS
MARGARITA
ALEMA
BARBECUE
TROPICAL
SUPREMA
LOUISIANA
CHEESEHAM
EUROPA
HAVAIANA
CAMPONESA
【样例输出】
0 15 G
2 11 C
7 18 A
4 8 C
16 13 B
4 15 E
10 3 D
5 1 E
19 7 C
11 11 H

思路

最直观的就是暴力枚举。

AC自动机就是更优秀的暴力(在这道题是这样的),先 b f s bfs 预处理出 f a i l fail 指针,由于 f a i l fail 指针是向深度减小方向的,因此保证了正确性。

这道题如果正着搜的话,找到最后一个字符,还有往前递推,很麻烦,因此采用倒着搜。

方向性有点麻烦,请务必考虑完状态。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
const int N=1e6+10;
const int M=1e3+10;
const int dx[8]={-1,-1,0,1,1,1,0,-1};
const int dy[8]={0,1,1,1,0,-1,-1,-1};
struct node{int s,fail,c[27];}t[N];int cnt;
void build(char *s,int id)
{
	int len=strlen(s+1),x=0;
	for(int i=len;i>=1;i--)
	{
		int y=s[i]-'A'+1;
		if(!t[x].c[y])t[x].c[y]=++cnt;
		x=t[x].c[y];
	}
	t[x].s=id;
}
int q[N],l,r;
void bfs()
{
	 l=1;r=2;q[1]=0;
	 while(l!=r)
	 {
	 	int x=q[l++];
	 	for(int i=1;i<=26;i++)
	 	{
	 		if(!t[x].c[i])continue;
	 		int y=t[x].c[i];
	 		if(!x)t[y].fail=0;
	 		else
	 		{
	 			int k=t[x].fail;
	 			while(k&&!t[k].c[i])k=t[k].fail;
	 			t[y].fail=t[k].c[i];
	 		}
	 		q[r++]=y;
	 	}
	 }
}
struct answer{int x,y,f;}ans[M];
int n,m,w,tot;char s[M][M];char ss[M];
bool check(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}
void find(int x,int y,int f)
{
	int u=0;
	while(check(x,y))
	{
		char v=s[x][y]-'A'+1;
		while(u&&!t[u].c[v])u=t[u].fail;
		if(t[u].c[v])u=t[u].c[v];
		int k=u;
		while(k)
		{
			if(t[k].s)
			{
				--tot;
				int id=t[k].s;
				ans[id].x=x,ans[id].y=y,ans[id].f=(f+4)%8;
			}
			t[k].s=0;
			k=t[k].fail;
		}
		x+=dx[f],y+=dy[f];
	}
}
//const int dx[8]={-1,-1,0,1,1, 1, 0,-1};
//const int dy[8]={0,  1,1,1,0,-1,-1,-1};
int main()
{
	scanf("%d%d%d",&n,&m,&w);tot=w;
	for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
	for(int i=1;i<=w;i++){scanf("%s",ss+1);build(ss,i);}
	bfs();
	for(int i=1;i<=n;i++)if(tot)find(i,1,1),find(i,1,2),find(i,1,3),find(i,m,5),find(i,m,6),find(i,m,7);
	for(int i=1;i<=m;i++)if(tot)find(1,i,3),find(1,i,4),find(1,i,5),find(n,i,0),find(n,i,1),find(n,i,7);
	for(int i=1;i<=w;i++)
		printf("%d %d %c\n",ans[i].x-1,ans[i].y-1,ans[i].f+'A');
	return 0;
}

caioj1466

【题意】
给出n个模式串,然后给出一个修改串,求尽量少修改修改串,使得修改串不含有任何一个模式串,不能的话输出-1
每个串只有’A’,‘C’,‘G’,'T’四个字母
【输入格式】
有多组数据,输入以一个0结束
每组数据:
输入一个n(n<=50)
接下来n行输入n个模式串(每个模式串长度不超过20)
最后一行输入修改串(长度不超过1000)
【输出格式】
输出Case T: ans
T当前输出的是第T组数据,ans表示最少修改次数,不能修改则ans=-1
【样例输入】
2
AAA
AAG
AAAG
2
A
TG
TGAATG
4
A
G
C
T
AGT
0
【样例输出】
Case 1: 1
Case 2: 4
Case 3: -1

思路

分析一下题意,就是说修改串不能含有一个完整的模式串,转化成AC自动机,就是不能含有字符串结束节点,也就是 t [ x ] . s ! = 0 t[x].s!=0 的点要避开,不能经过。

可能有些模式串 s 1 s_1 是另一模式串 s 2 s_2 的真子串,那么在匹配的最后一个字符那处,也要让 t [ x ] . s = 1 t[x].s=1 ,因为同样不能经过这里。

若当前节点 x x 没有 i i 这个儿子,若有失败指针 f a i l fail ,那么将 t [ t [ x ] . f a i l ] . c [ i ] t[t[x].fail].c[i] 认作儿子

之后就是推方程了,由于不能经过结束节点,方程为:

d p [ i + 1 ] [ j ] = min { d p [ i + 1 ] [ j ] , d p [ i ] [ t [ j ] . c [ k ] ] + ( y s [ s [ i + 1 ] ! = k ) } dp[i+1][j]=\min\begin{Bmatrix}dp[i+1][j],dp[i][t[j].c[k]]+(ys[s[i+1]!=k)\end{Bmatrix}

解释:在修改串的位置 1 1 ,到 t [ j ] . c [ i ] t[j].c[i] 的最少修改次数。

有些节点儿子会指向根节点,没有关系,这说明了匹配完了,返回根节点,重新匹配。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=1e3+10;
const int M=2e3+10;
struct Trie{int sum,fail,c[5];}t[N];int cnt;
int ys[150];char s[M];
inline void clear(int x){t[x].sum=t[x].fail=0;memset(t[x].c,0,sizeof(t[x].c));}
void build(char *s)
{
	int len=strlen(s+1),x=0;
	for(int i=1;i<=len;i++)
	{
		int y=ys[s[i]];
		if(!t[x].c[y])t[x].c[y]=++cnt,clear(cnt);
		x=t[x].c[y];
	}
	++t[x].sum;
}
int q[N],l,r;
void bfs()
{
	l=1;r=2;
	while(l!=r)
	{
		int x=q[l++];
		for(int i=1;i<=4;i++)
		{
			if(!t[x].c[i])
			{
				if(x)
				{
					int k=t[x].fail;
					while(k&&!t[k].c[i])k=t[k].fail;
					t[x].c[i]=t[k].c[i];
				}
				continue;
			}
			int y=t[x].c[i];
			if(!x)t[y].fail=0;
			else
			{
				int k=t[x].fail;
				while(k&&!t[k].c[i])k=t[k].fail;
				t[y].fail=t[k].c[i];
				if(t[t[y].fail].sum)t[y].sum=1;
			}
			q[r++]=y;
		}
	}
}
int inf,f[M][N];
void solve(char *s)
{
	int len=strlen(s+1);
	for(int i=0;i<len;i++)
	{
		for(int j=0;j<=cnt;j++)
		{
			if(f[i][j]==inf)continue;
			for(int k=1;k<=4;k++)
			{
				int y=t[j].c[k];
				if(t[y].sum)continue;
				f[i+1][y]=min(f[i+1][y],f[i][j]+(ys[s[i+1]]!=k));
			}
		}
	}
	int ans=inf;
	for(int i=0;i<=cnt;i++)ans=min(ans,f[len][i]);
	if(ans==inf)puts("-1");
	else printf("%d\n",ans);
}
int main()
{
	ys['A']=1;ys['C']=2;ys['G']=3;ys['T']=4;
	for(int T=1;;++T)
	{
		clear(0);
		int n;scanf("%d",&n);if(!n)break;
		cnt=0;
		for(int i=1;i<=n;i++){scanf("%s",s+1);build(s);}
		bfs();memset(f,63,sizeof(f));f[0][0]=0;inf=f[0][1];scanf("%s",s+1);
		printf("Case %d: ",T);
		solve(s);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/99826842