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的第一个字符的情况就更新不到