CF1163D Mysterious Code

一道十分可做的纸糊串题
题面
首先涉及到匹配那一定用AC自动机来解决
可以想到在s串的末尾节点上打个1,t串末尾打个-1,那么把c串在上面跑匹配,最终统计的结果就是s的出现次数-t的出现次数
定义状态f[i][j]表示在c串的第i个位置,AC自动机上的第j个节点的最大结果
在AC自动机上跑,能匹配就跳,加上这个点的权值
但如果出现了如第二个的数据,那么我们这种做法似乎不对
因为权值统计的出现问题
我们如果只是像上述那样标记权值的话,那么第二个a只会被算一次,所以有些点也必须标记权值
自然是从哪个点来,就加上那个点的权值
代码

#include<bits/stdc++.h>
using namespace std;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
const int N=4e3+8;
int n;
#define inf 0x7f7f7f7f
struct AC {
	int son[N][28],fail[N],cnt,w[N],bfn[N],lenth;
	void insert(int l,string s,int opt) {
		int p=0;
		for(int i=0;i<l;i++) {
			char c=s[i]-'a'+1;
			if(!son[p][c]) son[p][c]=++cnt;
			p=son[p][c];
		}
		w[p]+=1*opt;
	}
	void bfs() {
		queue <int> q;
		bfn[++lenth]=0;
		for(int i=1;i<=26;i++) {
			if(son[0][i]) q.push(son[0][i]),bfn[++lenth]=son[0][i];
		}
		while(q.size()) {
			int p=q.front();
			bfn[++lenth]=p;
			q.pop();
			for(int i=1;i<=26;i++) {
				if(son[p][i]) {
					fail[son[p][i]]=son[fail[p]][i];
					q.push(son[p][i]);
				}
				else son[p][i]=son[fail[p]][i];
			}
		}
		for(int i=1;i<=lenth;i++) {
			w[bfn[i]]+=w[fail[bfn[i]]];
		}
	}
} wgj;
int f[N][N];
string s,t;
char c[2000];
int main() {
	scanf("%s",(c+1));n=strlen(c+1);
	cin>>s;wgj.insert(s.size(),s,1);
	cin>>t;wgj.insert(t.size(),t,-1);
	wgj.bfs();
	for(int i=0;i<=n;i++) {
		for(int j=0;j<=wgj.cnt;j++) {
			f[i][j]=-inf/3;
		}
	}
	f[0][0]=0;
	for(int i=0;i<n;i++) {
		for(int j=0;j<=wgj.cnt;j++) {
			if(f[i][j]==-inf/3) continue;
			for(int ch=1;ch<=26;ch++) {
				int to=wgj.son[j][ch];
				if(c[i+1]==ch+'a'-1||c[i+1]=='*')
					f[i+1][to]=max(f[i+1][to],f[i][j]+wgj.w[to]);
			}
		}
	}
	int ans=-inf;
	for(int i=0;i<=wgj.cnt;i++) {
		ans=max(ans,f[n][i]);
	}
	cout<<ans<<endl;
	return 0;
}

有一个细节,就是c字符串的下标必须从1开始,因为你是通过\(f[0][0]\)去更新每一个\(f[i][j]\),如果不从1开始,可能c的第一个字符的情况就更新不到

posted @ 2021-06-13 11:25  redproblemdog  阅读(72)  评论(0编辑  收藏  举报