Description
在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。
一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
题解:
对于前两问,我们考虑字符串dp即可,即最短不公共子串长度=最长匹配中的最小值+1。
第一问 :A的一个最短的子串,它不是B的子串
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示A字符串的第i位匹配到字符串B的第j位的最长公共公共长度
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
+
1
(
A
[
i
]
=
=
B
[
j
]
)
dp[i][j]=dp[i-1][j-1]+1(A[i]==B[j])
dp[i][j]=dp[i−1][j−1]+1(A[i]==B[j])
因为是子串匹配子串,所以枚举字符串B时,如果此时失配了,就应该直接置0
第二问 : A的一个最短的子串,它不是B的子序列
我们只需要在第一问的基础上增加一个
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
−
1
]
(
A
[
i
]
!
=
B
[
j
]
)
dp[i][j]=dp[i][j-1](A[i]!=B[j])
dp[i][j]=dp[i][j−1](A[i]!=B[j]) 即可,因为我们的字符串要求的是子序列,所以我们前面匹配到了几个字符继承就行了。
第三问:A的一个最短的子序列,它不是B的子串
我们考虑用
B
B
B串建立
S
A
M
SAM
SAM,设f[i]表示
A
A
A的子序列匹配到
S
A
M
SAM
SAM结点i能得到的最短长度
我们可以枚举A的每一位
A
[
i
]
A[i]
A[i]和
S
A
M
SAM
SAM中的每一个结点j
如果有
n
x
t
[
j
]
[
A
[
i
]
−
′
a
′
]
nxt[j][A[i]-'a']
nxt[j][A[i]−′a′]这个结点,说明可以匹配上
f
[
n
x
t
[
j
]
[
A
[
i
]
−
′
a
′
]
]
=
m
i
n
(
f
[
n
x
t
[
j
]
[
A
[
i
]
−
′
a
′
]
]
,
f
[
j
]
+
1
)
f[nxt[j][A[i]-'a']] = min(f[nxt[j][A[i]-'a']],f[j]+1)
f[nxt[j][A[i]−′a′]]=min(f[nxt[j][A[i]−′a′]],f[j]+1)即可
要是没有这个结点,则失配记录
m
i
n
min
min
第四问: A的一个最短的子序列,它不是B的子序列
两个都是子序列,我们考虑用
B
B
B串建立序列自动机
序列自动机可以通过记录第i个位置后字符j最早出现的位置,求出一个串的所有子序列,就是这么简单…时间复杂度
O
(
26
N
)
O(26N)
O(26N)
那么我们建立了序列自动机还得到了一个数组
c
[
i
]
[
j
]
c[i][j]
c[i][j]表示第
i
i
i位字符之后字母j的最近位置
然后我们让
A
A
A串的子序列在序列自动机上匹配
设
f
[
i
]
f[i]
f[i]表示
A
A
A串的子序列匹配到字符串
B
B
B的第
i
i
i位的最短长度
我们就可以枚举
A
A
A的每一位
i
i
i,倒序枚举
B
B
B的每一位
j
j
j
如果有
c
[
j
]
[
A
[
i
]
−
′
a
′
]
c[j][A[i]-'a']
c[j][A[i]−′a′]这个结点,说明j之后有一个位置能与
A
[
i
]
A[i]
A[i]匹配上
f
[
c
[
j
]
[
s
1
[
i
]
−
′
a
′
]
]
=
m
i
n
(
f
[
c
[
j
]
[
s
1
[
i
]
−
′
a
′
]
]
,
f
[
j
]
+
1
)
f[c[j][s1[i]-'a']] = min(f[c[j][s1[i]-'a']],f[j]+1)
f[c[j][s1[i]−′a′]]=min(f[c[j][s1[i]−′a′]],f[j]+1)
即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4005;
const int INF = 0x3f3f3f3f;
char s1[MAXN],s2[MAXN];
int len1,len2,last=1,tot=1;
int len[MAXN],fa[MAXN],nxt[MAXN][26];
int dp[MAXN][MAXN],f[MAXN];
int c[MAXN][MAXN],mp[30];
inline void solve1(){
memset(dp,0,sizeof(dp));
int ans = INF;
for(int i=1;i<=len1;i++){
int mx = 0;
for(int j=1;j<=len2;j++){
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1;
mx = max(mx,dp[i][j]);
}
if(mx < i) ans = min(ans,mx+1);
}
if(ans>len1 || ans>len2) puts("-1");
else printf("%d\n",ans);
}
inline void solve2(){
memset(dp,0,sizeof(dp));
int ans = INF;
for(int i=1;i<=len1;i++){
int mx = 0;
for(int j=1;j<=len2;j++){
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=dp[i][j-1];
mx = max(mx,dp[i][j]);
}
if(mx < i) ans = min(ans,mx+1);
}
if(ans>len1 || ans>len2) puts("-1");
else printf("%d\n",ans);
}
inline void solve3(){
memset(f,INF,sizeof(f));
f[1]=0;
int ans = INF;
for(int i=1;i<=len1;i++)
for(int j=1;j<=tot;j++)
if(!nxt[j][s1[i]-'a']) ans = min(ans,f[j]+1);
else f[nxt[j][s1[i]-'a']] = min(f[nxt[j][s1[i]-'a']],f[j]+1);
if(ans>len1 || ans>len2) puts("-1");
else printf("%d\n",ans);
}
inline void solve4(){
memset(f,INF,sizeof(f));
f[0]=0;
for(int i=len2;i>=0;i--){
for(int j=0;j<26;j++)
if(mp[j]) c[i][j]=mp[j];
mp[s2[i]-'a']=i;
}
int ans = INF;
for(int i=1;i<=len1;i++)
for(int j=len2;j>=0;j--)
if(!c[j][s1[i]-'a']) ans = min(ans,f[j]+1);
else f[c[j][s1[i]-'a']] = min(f[c[j][s1[i]-'a']],f[j]+1);
if(ans>len1 || ans>len2) puts("-1");
else printf("%d\n",ans);
}
inline void Insert(int x){
int p=last,np=++tot;
last=np,len[np]=len[p]+1;
for(;p&&!nxt[p][x];p=fa[p]) nxt[p][x]=np;
if(!p) fa[np]=1;
else{
int q = nxt[p][x];
if(len[p]+1==len[q]) fa[np]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;
memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for(;nxt[p][x]==q;p=fa[p]) nxt[p][x]=nq;
}
}
}
int main(){
scanf("%s%s",s1+1,s2+1);
len1=strlen(s1+1),len2=strlen(s2+1);
for(int i=1;i<=len2;i++) Insert(s2[i]-'a');
solve1();
solve2();
solve3();
solve4();
return 0;
}