HDU7015 - Another String

Description

给出一个长度为\(n\)的字符串\(s\)和一个正整数\(k\)。定义两个长度相同且相差不超过\(k\)个字符的字符串是”k-matching“的,对于每个\(i\in[1,n-1]\),求\(s_{1..i}\)的子串和\(s_{i+1..n}\)的子串有多少对是”k-matching“的。

Solution

反过来考虑每一对“k-matching”的子串对那些答案有贡献。首先搞出一个数组\(f(i,j)\)​​​​​,记录\(s_{i...}\)​​​​​和\(s_{j...}\)​​​​​最远能匹配的长度,可以固定\(j-i\)​​再用two-pointers解决。那么对于每对\(i,j\)​,它对答案的贡献如下:

i-1 i i+1 ... i+f-2 i+f-1 i+f ... j-1 j j+1
\(ans\) 0 +1 +2 ... +f-1 +f +f +f +f 0 0
差分 0 +1 +1 +1 +1 +1 0 0 0 -f 0
再差分 0 +1 0 0 0 0 -1 0 0 -f +f

于是其对再差分数组的影响是\(O(1)\)的​,用\(f(i,j)\)更新完再差分数组再求两遍前缀和即可。

时间复杂度\(O(n^2)\)

Code

//Another String
#include <algorithm>
#include <cstdio>
using std::min;
const int N=3e3+10;
int n,k; char s[N];
int f[N][N]; long long ans[N];
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
    
    scanf("%d%d",&n,&k); scanf("%s",s+1);
    for(int d=1;d<=n;d++)
    {
        int len=0,cnt=0;
        for(int i=1;i+d<=n;i++)
        {
            int j=i+d;
            while(cnt<=k) cnt+=(s[i+len]!=s[j+len]),len++;
            f[i][j]=min(len-1,min(d,n-j+1));
            len--,cnt-=(s[i]!=s[j]);
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            int len=f[i][j];
            ans[i]++;
            ans[i+len]--;
            ans[j]-=len;
            ans[j+1]+=len;
        }
    for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
    for(int i=1;i<n;i++) printf("%lld\n",ans[i]);
    
    for(int i=0;i<=n+2;i++) ans[i]=0; 

    }
    return 0;
}

Description

给出一个长度为\(n\)的字符串\(s\)和一个正整数\(k\)。定义两个长度相同且相差不超过\(k\)个字符的字符串是”k-matching“的,对于每个\(i\in[1,n-1]\),求\(s_{1..i}\)的子串和\(s_{i+1..n}\)的子串有多少对是”k-matching“的。

Solution

反过来考虑每一对“k-matching”的子串对那些答案有贡献。首先搞出一个数组\(f(i,j)\)​​​​​,记录\(s_{i...}\)​​​​​和\(s_{j...}\)​​​​​最远能匹配的长度,可以固定\(j-i\)​​再用two-pointers解决。那么对于每对\(i,j\)​,它对答案的贡献如下:

\(i-1\) \(i\) \(i+1\) ... \(i+f-2\) \(i+f-1\) \(i+f\) ... \(j-1\) \(j\) \(j+1\)
\(ans\) 0 +1 +2 ... +f-1 +f +f +f +f 0 0
差分 0 +1 +1 +1 +1 +1 0 0 0 -f 0
再差分 0 +1 0 0 0 0 -1 0 0 -f +f

于是其对再差分数组的影响是\(O(1)\)的​,用\(f(i,j)\)更新完再差分数组再求两遍前缀和即可。

时间复杂度\(O(n^2)\)

Code

//Another String
#include <algorithm>
#include <cstdio>
using std::min;
const int N=3e3+10;
int n,k; char s[N];
int f[N][N]; long long ans[N];
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
    
    scanf("%d%d",&n,&k); scanf("%s",s+1);
    for(int d=1;d<=n;d++)
    {
        int len=0,cnt=0;
        for(int i=1;i+d<=n;i++)
        {
            int j=i+d;
            while(cnt<=k) cnt+=(s[i+len]!=s[j+len]),len++;
            f[i][j]=min(len-1,min(d,n-j+1));
            len--,cnt-=(s[i]!=s[j]);
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            int len=f[i][j];
            ans[i]++;
            ans[i+len]--;
            ans[j]-=len;
            ans[j+1]+=len;
        }
    for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
    for(int i=1;i<n;i++) printf("%lld\n",ans[i]);
    
    for(int i=0;i<=n+2;i++) ans[i]=0; 

    }
    return 0;
}
posted @ 2021-08-04 10:16  VisJiao  阅读(98)  评论(2编辑  收藏  举报