Another String | HDU7015 | 杭电多校五 04题 我们说两个序列
a
,
b
a,b
a,b 是
k
k
k 匹配的,当且仅当
∣
a
∣
=
∣
b
∣
|a|=|b|
∣a∣=∣b∣ 且
∑
i
[
a
i
≠
b
i
]
≤
k
\sum_{i} [a_i\ne b_i]\le k
∑i[ai=bi]≤k ,即最多有
k
k
k 个位置不同 给定一个字符串
S
S
S 和
k
k
k 你需要求出
∀
i
∈
[
1
,
∣
S
∣
−
1
]
\forall i\in[1,|S|-1]
∀i∈[1,∣S∣−1],串
S
[
1
,
i
]
S[1,i]
S[1,i] 的一个子串
a
a
a 与 串
S
[
i
+
1
,
∣
S
∣
]
S[i+1,|S|]
S[i+1,∣S∣] 的一个子串
b
b
b 是
k
k
k 匹配的,问有多少种方案
2
≤
∣
S
∣
≤
3000
2\le |S|\le 3000
2≤∣S∣≤3000
0
≤
k
≤
3000
0\le k\le 3000
0≤k≤3000
思路
感觉字符串匹配,可能就是字符串或者卷积的题 (???) 就不敢去看 但是其实这题的做法比较偏想法
首先我们很难按照
i
=
1
∼
∣
S
∣
−
1
i=1\sim |S|-1
i=1∼∣S∣−1 去分类讨论做 不妨设目前我们有两个子串
[
L
1
,
R
1
]
[L1,R1]
[L1,R1] 与
[
L
2
,
R
2
]
[L2,R2]
[L2,R2] 是
k
k
k 匹配的 那么我们能扩展或收缩到那些其他的子串,保证一定合法的呢?
容易想到,如果
L
1
,
R
1
L1,R1
L1,R1 向右移动一段距离,然后所有合法的子串中,会有许多重复 那么我们就固定左端点,计算此时有多少合法的右端点
然后去考虑,每一个合法的子串对那些
i
i
i 有贡献
对于第一个区间
[
L
1
,
R
1
]
[L1,R1]
[L1,R1],我们对
i
=
L
1
∼
L
2
−
1
i=L1\sim L2-1
i=L1∼L2−1 都有
1
1
1 的贡献 对于第
x
x
x 个区间
[
L
1
+
x
−
1
,
R
1
]
[L1+x-1,R1]
[L1+x−1,R1],我们对
i
=
L
1
+
x
−
1
∼
L
2
−
1
i=L1+x-1\sim L2-1
i=L1+x−1∼L2−1 都有
1
1
1 的贡献
考虑怎么去快速累加贡献 如果只是对一段区间
[
x
,
y
]
[x,y]
[x,y] 全部有
+
1
+1
+1 的贡献,我们可以用线段树,也可以用差分数组
d
e
[
x
]
+
1
,
d
e
[
y
+
1
]
−
1
de[x]+1,de[y+1]-1
de[x]+1,de[y+1]−1 去做 但是我们会有
x
x
x 个区间去做,容易想到去用二阶等差 让
d
e
2
[
L
1
]
+
1
,
d
e
2
[
R
1
+
1
]
−
1
de2[L1]+1,de2[R1+1]-1
de2[L1]+1,de2[R1+1]−1 然后我们让一阶等差
d
e
[
L
2
]
−
(
R
1
−
L
1
+
1
)
de[L2]-(R1-L1+1)
de[L2]−(R1−L1+1) ,这样就可以达到我们的要求
然后就是,对于所有的左端点
L
1
L1
L1,我们需要找到最大的
R
1
R1
R1 和另一个
L
2
L2
L2,满足上述区间有
k
k
k 个不同的位置,或者扩到不能扩为止 容易想到这是一个尺取,也就是我们的
[
L
1
,
R
1
]
[L1,R1]
[L1,R1] 可以在
O
(
∣
S
∣
)
O(|S|)
O(∣S∣) 内枚举完 那么久剩下
L
2
L2
L2 没有枚举了,我们令
L
2
=
L
1
+
x
L2=L1+x
L2=L1+x ,枚举这个
x
x
x ,即可枚举到所有的情况
代码
时间复杂度:
O
(
∣
S
∣
2
)
O(|S|^2)
O(∣S∣2)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =3e3+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;char ss[MAX];int de1[MAX],de2[MAX];intmain(){int T;scanf("%d",&T);while(T--){int n,k;scanf("%d%d",&n,&k);scanf("%s",ss+1);for(int i =0;i <= n+1;++i)de1[i]= de2[i]=0;for(int cha =1;cha <= n-1;++cha){int L1 =1,L2 = L1 + cha;int R1 = L1-1,R2 = L2-1;int now =0;while(L2 <= n){while(R1+1<= L2-1&& R2+1<= n){if(ss[R1+1]== ss[R2+1])R1++,R2++;elseif(now < k)now++,R1++,R2++;elsebreak;}if(L1 <= R1 && now <= k){int len = R1 - L1 +1;
de2[L1]++;
de2[R1+1]--;
de1[L2]-=len;if(ss[L1]!= ss[L2])now--;
L1++;
L2++;}else{
L1++;
L2++;
R1 = L1;R2 = L2;if(ss[L1]== ss[L2])now =0;else now =1;}}}
ll tmp =0,ans =0;for(int i =1;i <= n;++i)tmp += de2[i],de1[i]+= tmp;
tmp =0;for(int i =1;i < n;++i)ans += de1[i],printf("%lld\n",ans);}return0;}