我的做法竟然跟官方题解一样 /jk
赛时的沧桑路程: O ( n 4 ) → O ( n 3 ) → O ( n 2 log ( n 2 ) ) \mathcal O(n^4) \rarr \mathcal O(n^3) \rarr \mathcal O(n^2 \log (n^2)) O(n4)→O(n3)→O(n2log(n2))
O
(
n
4
)
\mathcal O(n^4)
O(n4):
考虑每一个
T
T
T 的连续子串,用
O
(
n
2
)
\mathcal O(n^2)
O(n2) 的DP来计算它是否为
S
S
S 的子串。
我们记当前枚举到的
T
T
T 的连续子串为
f
f
f
设
f
i
,
j
=
0
/
1
f_{i,j}=0/1
fi,j=0/1 表示
f
1
…
j
f_{1 \dots j}
f1…j 是否是
S
1
…
i
S_{1 \dots i}
S1…i 的子串
转移方程:
f
i
,
j
=
max
k
=
0
i
−
1
f
k
,
j
−
1
f_{i,j}= \max\limits_{k=0}^{i-1}f_{k,j-1}
fi,j=k=0maxi−1fk,j−1
这里的转移可以通过前缀和优化做到
O
(
n
2
)
\mathcal O(n^2)
O(n2) 的DP复杂度
最后统计答案的时候哈希去一下重就可以了
O ( n 3 ) \mathcal O(n^3) O(n3):
由于刚刚的枚举的大部分
f
f
f 都互相满足前缀包含关系,所以我们可以考虑只在
T
T
T 中枚举连续子串的左端点
l
l
l
设
f
i
,
j
=
0
/
1
f_{i,j}=0/1
fi,j=0/1 表示
T
l
…
l
+
j
−
1
T_{l \dots l+j-1}
Tl…l+j−1 是否为
S
1
…
i
S_{1 \dots i}
S1…i 的子串,转移方程同上。
统计答案时同样需要去重
O ( n 2 log ( n 2 ) ) \mathcal O(n^2 \log (n^2)) O(n2log(n2))
前面,我们DP考虑的都是字符串的完美匹配。现在,我们将状态改为如下:
求一个尽可能大的
f
i
,
j
f_{i,j}
fi,j,满足所有以
T
j
T_j
Tj 为右端点,且长度不大于
f
i
,
j
f_{i,j}
fi,j 的所有
T
T
T 的连续子串都是
S
1
…
i
S_{1\dots i}
S1…i 的子串。
转移方程:
f
i
,
j
=
max
k
=
0
i
−
1
f
k
,
j
−
1
+
1
f_{i,j}= \max\limits_{k=0}^{i-1}f_{k,j-1}+1
fi,j=k=0maxi−1fk,j−1+1,其中加上的
1
1
1 为子串
T
[
j
,
j
]
T[j,j]
T[j,j] 的贡献。这里也可以用同上的方法做到
O
(
n
2
)
\mathcal O(n^2)
O(n2)
现在,我们可以根据 f f f 数组得到所有同时是 S S S 的子串也是 T T T 的连续子串的字符串,可知这些字符串的总数为不超过 n 2 n^2 n2。那么,我们只需要需要通过哈希求出不同的字符串个数就可以了。
由于不知道怎样
O
(
n
2
)
\mathcal O(n^2)
O(n2) 哈希加去重,又不敢用 unordered_map
。所以赛时只好写了一个
O
(
n
2
log
(
n
2
)
)
\mathcal O(n^2 \log (n^2))
O(n2log(n2)) 的 sort + unique
,希望人没事 /kk
PS: 在洛谷上不吸氧只能有90 /dk
//stage three complete
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int Maxn=3010;
const int Maxm=9000010;
typedef unsigned long long ull;
const ull base=27;
char a[Maxn],b[Maxn];
int s[Maxn],f[Maxn][Maxn];
int p[Maxn];
ull c[Maxm];
int n;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("block.in","r",stdin);
// freopen("block.out","w",stdout);
n=read();
scanf("%s",a+1);
scanf("%s",b+1);
for(int j=1;j<=n;++j)
{
for(int i=1;i<=n;++i)
if(a[i]==b[j])
f[i][j]=s[i-1]+1;
for(int i=1;i<=n;++i)
{
s[i]=max(s[i-1],f[i][j]);
p[j]=max(p[j],f[i][j]);
}
}
int cnt=0;
for(int i=1;i<=n;++i)
{
ull tmp=0;
for(int j=i;j>i-p[i];--j)
{
tmp=tmp*base+(b[j]-'a'+1);
c[++cnt]=tmp;
}
}
sort(c+1,c+1+cnt);
cnt=unique(c+1,c+1+cnt)-(c+1);
printf("%d\n",cnt);
return 0;
}