CCPC2022 网络赛 Substring Match
Substring Match
给定长为\(n\)的文本串\(S\)和长为\(m\)的模式串\(T\),求\(T\)在\(S\)中能匹配的最长的子串的长度。
\(T\)中有不超过200个大写字母。大写字母能匹配任意数量(包括\(0\))个对应的小写字母。
\(1\leq n,m\leq 10^5\)
题解
这个大写字母的匹配显然不能贪心。
考虑到大写字母数量比较少,可以将\(T\)按照单个大写字母和连续小写字母分段,然后DP。也就是把这个是否贪心的判断交给程序。
设\(f(i, j)\)表示\(S\)的第\(i\)个位置匹配到\(T\)的第\(j\)段能匹配的最大长度。
为了\(O(1)\)转移,我们需要预处理每个连续小写字母段在\(S\)中能匹配的位置。
时间复杂度\(O(200n)\)。
constexpr int N=1e5+10, INF=1e9;
char s[N], t[N], r[N];
struct Node{
int type, l, r; // 1 for abc, 2 for A
}a[410];
int mt[410][N], nxt[N];
void match(int n, int m, int k){
fill(mt[k], mt[k]+n+1, 0);
for(int i=2, j=0; i<=m; ++i){
while(j && r[i]!=r[j+1]) j=nxt[j];
if(r[i]==r[j+1]) ++j;
nxt[i]=j;
}
for(int i=1, j=0; i<=n; ++i){
while(j && (j==m || s[i]!=r[j+1])) j=nxt[j];
if(s[i]==r[j+1]) ++j;
if(j==m) mt[k][i-m+1]=1;
}
}
int f[N][410];
void cmax(int &x, int y){
x=max(x, y);
}
void realMain(){
scanf("%s%s", s+1, t+1);
int n=strlen(s+1), m=strlen(t+1), cnt=0;
for(int l=1, r; l<=m; l=r+1){
if(isupper(t[l])){
r=l;
a[++cnt]={2, l, r};
continue;
}
for(r=l; r+1<=m && islower(t[r+1]); ++r);
a[++cnt]={1, l, r};
}
for(int i=1; i<=cnt; ++i)if(a[i].type==1){
copy(t+a[i].l, t+a[i].r+1, r+1);
match(n, a[i].r-a[i].l+1, i);
}
for(int i=0; i<=n; ++i) fill(f[i]+1, f[i]+cnt+1, -INF);
for(int i=0; i<=n; ++i)
for(int j=0; j<=cnt; ++j)if(f[i][j]>-INF){
if(i<n && a[j].type==2 && s[i+1]==tolower(t[a[j].l]))
cmax(f[i+1][j], f[i][j]+1);
if(j<cnt && a[j+1].type==2)
cmax(f[i][j+1], f[i][j]);
if(i<n && j<cnt && a[j+1].type==1 && mt[j+1][i+1])
cmax(f[i+a[j+1].r-a[j+1].l+1][j+1], f[i][j]+a[j+1].r-a[j+1].l+1);
if(i<n && j<cnt && a[j+1].type==2 && s[i+1]==tolower(t[a[j+1].l]))
cmax(f[i+1][j+1], f[i][j]+1);
}
int ans=0;
for(int i=1; i<=n; ++i) cmax(ans, f[i][cnt]);
printf("%d\n", ans);
}
int main(){
for(int t=read<int>(); t--; ) realMain();
return 0;
}
考试时写代码出了一系列问题。比如不会写KMP,f[i][0]
初值设成-INF
,ans
初值设成-INF
。
静渊以有谋,疏通而知事。