【训练题55:尺取 + 高阶等差】Another String | HDU7015 | 杭电多校五 04题

题意

  • 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,S1],串 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 2S3000
    0 ≤ k ≤ 3000 0\le k\le 3000 0k3000

思路

  • 感觉字符串匹配,可能就是字符串或者卷积的题 (???) 就不敢去看
    但是其实这题的做法比较偏想法
  • 首先我们很难按照 i = 1 ∼ ∣ S ∣ − 1 i=1\sim |S|-1 i=1S1 去分类讨论做
    不妨设目前我们有两个子串 [ 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=L1L21 都有 1 1 1 的贡献
    对于第 x x x 个区间 [ L 1 + x − 1 , R 1 ] [L1+x-1,R1] [L1+x1,R1],我们对 i = L 1 + x − 1 ∼ L 2 − 1 i=L1+x-1\sim L2-1 i=L1+x1L21 都有 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](R1L1+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(S2)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 3e3+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

char ss[MAX];
int de1[MAX],de2[MAX];
int main()
{
    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++;
                    else if(now < k)now++,R1++,R2++;
                    else break;
                }
                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);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值