题意:给定一个文本串,两个模式串,将文本串的*替换为26个 字母,使得第一个模式串出现的次数减去第二个模式串出现的次数的差值最大。
思路:fail树的应用
我们对两个模式串建立AC自动机,并计算出每个状态的权值。每个状态的权值定义为其fail树上一直到根节点的权值之和。原因为走到这个节点,就相当于到根节点路径上的的所有前缀都已经访问过了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int N = 1005;
const int inf = 0x3f3f3f3f;
char str[maxn],s1[maxn],s2[maxn];
int ch[N][26],tot,f[N],val[N],last[N],vis[N];
void init(){
tot = 0;
memset( ch[0],0,sizeof(ch[0]) );
}
void Insert( char* str,int v ){
int p = 0;int n = strlen(str);
for( int i = 0;i < n;i++ ){
int c= str[i]-'a';
if( !ch[p][c] ){
ch[p][c] = ++tot;val[tot] = 0;last[tot] = 0;f[tot] = 0;
memset( ch[tot],0,sizeof(ch[tot]) );
}
p = ch[p][c];
}
val[p] += v;
}
queue<int> que,Q;
void getfail(){
for( int i = 0;i < 26;i++ ) {
if( ch[0][i] ) {
que.push( ch[0][i] );
Q.push( ch[0][i] );
}
}
while(que.size()){
int x= que.front();
que.pop();
for( int c = 0;c < 26;c++ ){;
if(!ch[x][c]){
ch[x][c] = ch[f[x]][c];
continue;
}
f[ch[x][c]] = ch[f[x]][c];
last[ch[x][c]] = val[f[ch[x][x]]] ? f[ch[x][c]] : last[f[ch[x][c]]];
que.push( ch[x][c] );Q.push( ch[x][c] );
}
}
}
int dp[maxn][maxn],len;
int dfs( int x,int p ){
if( dp[x][p] != -inf ) return dp[x][p];
if( p == len ){
return val[x];
}
if( str[p] == '*' ){
int mx = -inf;
for( int c = 0;c < 26;c++ ){
mx = max( mx,dfs( ch[x][c],p+1 ) );
}
return dp[x][p] = val[x] + mx;
}else{
int res = dfs( ch[x][ str[p]-'a' ],p+1 );;
return dp[x][p] = val[x] + res;
}
}
int main(){
scanf("%s%s%s",str,s1,s2);
init();
Insert(s1,1);Insert(s2,-1);
getfail();
while(Q.size()){
int x = Q.front();
Q.pop();
val[x] += val[f[x]];
}
len = strlen(str);
for( int i = 0;i <= tot;i++ ){
for( int j = 0;j <= len;j++ ){
dp[i][j] = -inf;
}
}
int ans = dfs( 0,0 );
printf("%d\n",ans);
return 0;
}