题意简述:
定义
f
(
x
,
y
)
f(x,y)
f(x,y) 为
y
y
y 在
x
x
x 中出现次数
将
c
c
c 串中的星号用小写字母替换
要求使
f
(
c
,
s
)
−
f
(
c
,
t
)
f(c,s)-f(c,t)
f(c,s)−f(c,t) 最大并输出这个值
分析:
定义问题类型
这是一个典型的多字符串包含问题,那么显然是一个AC自动机
具体实现
首先处理AC自动机上的节点权值情况。在字典树上,我们让串 s s s 的终点权值为 1 1 1, t t t 的终点权值为 − 1 -1 −1,这样把 c c c 串丢上去跑匹配,可以直接出结果。
那么其他节点的权值呢?
考虑当前串会包含fail树的前缀,会产生贡献,所以将权值赋为fail树的前缀和。
然后我们套个dp( AC自动机结合dp是较为常见的操作,这里 s , t s,t s,t 都只有50,所以AC自动机的节点数不超过100,显然可以dp)
定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示在 c c c 串的第 i i i 个位置,在ACAM上 j j j 个节点的最大值
那就直接能匹配就跳转,加上该点权值即可。
于是有转移:
d p [ i + 1 ] [ i d ] = m a x ( d p [ i + 1 ] [ i d ] , d p [ i ] [ j ] + v a l [ i d ] ) dp[i+1][id]=max(dp[i+1][id],dp[i][j]+val[id]) dp[i+1][id]=max(dp[i+1][id],dp[i][j]+val[id])
Code:
//Mysterious Code
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int NUM=1005;
queue<int> q;
int cnt,ans=-1e9;
char c[NUM],s[NUM],t[NUM];
int tr[105][30],word[105],fail[105],dp[NUM][105];
inline void insert(char *s,int val) {
int x=0,len=strlen(s);
for (int i=0;i<len;++i) {
int y=s[i]-'a';
if(!tr[x][y]) {
++cnt;
tr[x][y]=cnt;
}
x=tr[x][y];
}
word[x]+=val;
}
inline void build() {
for (int i=0;i<26;++i) {
if(tr[0][i]) {
fail[tr[0][i]]=0;
q.push(tr[0][i]);
}
}
while(!q.empty()) {
int x=q.front();
q.pop();
for (int i=0;i<26;++i) {
int &y=tr[x][i];
if(!y) {
y=tr[fail[x]][i];
} else {
fail[y]=tr[fail[x]][i];
q.push(y);
}
}
word[x]+=word[fail[x]];
}
}
inline void init() {
memset(dp,-INF,sizeof(dp));
dp[0][0]=0;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>c>>s>>t;
insert(s,1);
insert(t,-1);
build();
init();
int len=strlen(c);
for (int i=0;i<len;++i) {
for (int j=0;j<=cnt;++j) {
for (int k=0;k<26;++k) {
if(c[i]=='*' || c[i]==k+'a')
dp[i+1][tr[j][k]]=max(dp[i+1][tr[j][k]],dp[i][j]+word[tr[j][k]]);
}
}
}
for (int i=0;i<=cnt;++i)
ans=max(ans,dp[len][i]);
cout<<ans;
return 0;
}