• hdu7015 / 2021“MINIEYE杯”中国大学生算法设计超级联赛(5)1004 Another String(尺取法+二阶差分)


    https://acm.hdu.edu.cn/showproblem.php?pid=7015

    题意:

    定义2个长度相等的字符串距离为对应位置不相同的字符个数

    若两个字符串的距离<=k,则称他们是k相似的

    给出一个字符串,将他分割为A[1,i]和B[i+1,n]两部分,

    问从A、B中各选一个子串,有多少对满足是k相似的

    对于i∈[1,n-1]依次回答

    令f[i][j]表示从字符串的i位置和j位置开始,能够满足k相似的最长长度

    可以用尺取法在O(n^2)的复杂度中求出来

    具体做法是枚举两个位置的间隔距离d

    枚举i和j,i从1开始,j从i+d开始

    若f[i][j]=L,那么f[i+1][j+1]就是L减去位置i和位置j的距离,再继续往后匹配

    对于每个d,匹配的最后位置单调不减

    设ans[i]表示在i后面分割,能找出的k相似字符串对数

    枚举位置i和位置j,考虑以i和j为起始位置的字符串的贡献

    设f[i][j]=x

    也就是说 s[i]=s[j],s[i+1]=s[j+1],……,s[i+x-1]=s[j+x-1]

    所以如果分割位置在i,以i和j为起始位置的字符串会有1的贡献,即s[i]与s[j]

    如果分割位置在i+1,有2的贡献,即s[i]与s[j],s[i]s[i+1]与s[j]s[j+1]

    如果分割位置在i+2,有3的贡献

    ……

    直到分割位置在i+f[i][j]-1,有f[i][j]的贡献

    当分割位置在i+f[i][j]-1后面且在j前面,都是有f[i][j]的贡献

    当分割位置在j后面,因为要求分割位置左右各选一个子串,所以无贡献

    另外,j不能超过分割位置,所以f[i][j]与j-i取小

    我们看以i和j为起始位置的字符串对不同的分割位置产生的贡献:

    0 0 0 1 2 3 ……f[i][j]-2 f[i][j]-1 f[i][j] f[i][j] f[i][j] 0 0 0

    最前面的1是从分割位置i开始的,到分割位置i+f[i][j]-1贡献依次加1,然后从分割位置i+f[i][j]开始贡献都是f[i][j]不变,再从分割位置j后贡献都是0

    对这个贡献做一次差分得到0 0 0 1 1 1 …… 1 1 1 0 0 -f[i][j] 0 0

    再做一次差分得到二阶差分0 0 0 1 0 0 …… 0 0 0 -1 0 -f[i][j] f[i][j] 0

    对于每一对i和j,在答案的二阶差分里只修改4个位置

    i位置+1,i+f[i][j]位置-1,j位置-f[i][j],j+1位置+f[i][j]

    最后求两次前缀和还原答案序列

    小细节:

    在求f[i][j]时,匹配位置可能超过n

    在字符串的最后加一个特殊字符,然后限定匹配到n+1,因为程序写法是到不合法位置停止

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 3002
    
    char s[N];
    int f[N][N];
    
    long long ans[N];
    
    int main()
    {
        int T,n,m,dis,len,k;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            scanf("%s",s+1);
            for(int i=1;i<=n;++i)
                for(int j=i+1;j<=n;++j)
                    f[i][j]=0;
            for(int i=1;i<n;++i) ans[i]=0;
            s[n+1]='#';
            for(int d=1;d<n;++d)
            {
                dis=0;
                len=0;
                for(int i=1,j=i+d;j<=n;++i,++j)
                {
                    while(dis<=m && j+len<=n+1)
                    {
                        dis+=s[i+len]!=s[j+len];
                        ++len;
                    }
                    f[i][j]=len-1;
                    dis-=s[i]!=s[j];
                    len--;
                }
            }
            for(int i=1;i<=n;++i)
                for(int j=i+1;j<=n;++j)
                {
                    ans[i]++;
                    k=min(j-i,f[i][j]);
                    ans[i+k]--;
                    ans[j]-=k;
                    ans[j+1]+=k;
                }
            for(int i=2;i<n;++i) ans[i]+=ans[i-1];
            for(int i=2;i<n;++i) ans[i]+=ans[i-1];
            for(int i=1;i<n;++i) printf("%lld
    ",ans[i]);
        }
    }
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    Ubuntu安装搜狗输入法
    Ubuntu 命令
    ubuntu忽然不能登录,输入密码正确一直返回登录界面
    chmod用法
    Maven学习(六)maven使用中遇到的坑
    win10下装mysql-5.7.18-winx64
    Maven学习(五)使用Maven构建多模块项目
    Maven学习(四)eclipse创建maven项目
    Maven学习(三)maven原理概念详述
    Struts2+Hibernate4+Spring4框架整合搭建Java项目原型
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15168245.html
Copyright © 2020-2023  润新知