2019年华南理工大学程序设计竞赛(春季赛) K Parco_Love_String(dp+递推/后缀数组)

链接:https://ac.nowcoder.com/acm/contest/625/K
来源:牛客网
 

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

众所周知,在算法竞赛中,出题人对他出的题的难度往往存在错误的估计。比如出题人本想出个简单题,没想到却出成了重坑细节题;本想出个中等难度题,结果却变成了防AK题。因此,为了让一场比赛能有良好的体验,有一个靠谱的验题人是非常重要的。

CC出好题目后,便拿给小马哥看。不出所料,这些题目小马哥全都是看一眼就会做,但他觉得这对于参赛选手来说还是有点难。为了避免CC被喷成毒瘤出题人,小马哥准备加一道签到题。

小马哥有一个只由小写字母组成的字符串S,他会问你一些关于这个字符串的问题。小马哥每次提问时会给你一个整数i,意思是要把字符串S在第i和第i+1个字符之间切断,这样便得到两个新串S[1,i]及S[i+1,|S|]。记A=S[1,i],B=S[i+1,|S|]。现在,先考虑A的所有连续子串,再考虑B的所有连续子串,小马哥问你A,B之间相同子串对有多少个。

也就是说,小马哥想让你计算下面这个式子:

ans=∑1≤l1≤r1≤|A|∑1≤l2≤r2≤|B|[ A[l1,⋯,r1]==B[l2,⋯,r2] ]ans=∑1≤l1≤r1≤|A|∑1≤l2≤r2≤|B|[ A[l1,⋯,r1]==B[l2,⋯,r2] ]

其中,求和式右边的式子是这样一个函数:

[x]={1(x=True)0(x=False)[x]={1(x=True)0(x=False)

你能解决小马哥的问题,并签到成功吗?

输入描述:

第一行是一个字符串S(1≤|S|≤1000),保证只由小写字母组成。
第二行是一个正整数T(1≤T≤105),表示询问次数。
接下来T行,每行一个整数i(1≤i≤n−1),表示一个询问。

输出描述:

对于每个询问,输出一行一个整数表示对应询问的ans,意义如题目描述中所述。

示例1

输入

复制

ababa
4
1
2
3
4

输出

复制

2
4
4
2

说明

对于i=3这个询问,原串被拆分成 A=aba,B=ba。
此时A的所有子串为:a,a,b,ab,ba,aba;B的所有子串为:a,b,ba。
因此A,B之间相同子串对为:(a,a),(a,a),(b,b),(ba,ba),共计4个。

题意:中文题略。

思路:

考虑n<=1000,先对字符串做一个n方的dp,求出以每个位置j与每个位置i为结尾的最长公共子串长度,再求出以每个位置i与每个位置j为开始的最长公共子串长度。

对于第一个字母,直接统计它在后面的字符串中出现的次数即可。

对于第i(i>1)个字母,答案就是1~i-1的答案,再加上所有j=i+1 ~ n,以i,j为结尾的最长公共子串长度(长度最大为j-i),再减去所有j=1~i-1中与以i为开始有最长公共子串的的答案(长度最大为i-j)。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
 
char s[maxn];
int f[maxn][maxn],g[maxn][maxn];
int ans[maxn];
int n,q;
 
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++) if (s[i]==s[j])
        f[i][j]=f[i-1][j-1]+1;
    for (int i=n;i>=1;i--)
    for (int j=n;j>=1;j--) if (s[i]==s[j])
        g[i][j]=g[i+1][j+1]+1;
    for (int i=2;i<=n;i++) if (s[1]==s[i])
        ans[1]++;
    for (int i=2;i<=n;i++)
    {
        ans[i]=ans[i-1];
        for (int j=i+1;j<=n;j++)
            ans[i]+=min(f[i][j],j-i);
        for (int j=1;j<i;j++)
            ans[i]-=min(g[i][j],i-j);
    }
    scanf("%d",&q);
    while (q--)
    {
        int x;
        scanf("%d",&x);
        printf("%d\n",ans[x]);
    }
    return 0;
}

而我比赛的时候依然蠢得没想到这个做法。

参考POJ 3415 Common Substrings的后缀数组做法,本题是其k=1的特例,并且要手动拆字符串。。。

由于后缀数组的解法是O(n)的,所以只需要暴力枚举前串,再套板子就行了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
const int maxn = 1e5+10;
int n,lena;
char a[maxn],aa[maxn];
typedef pair<char,int> pii;
int A[maxn],B[maxn];
int realrank[maxn],k,Len;
pii st[maxn];
int s[maxn][2];
long long h[maxn];
int K;
int C[maxn],D[maxn];
ll anss[maxn];
void init(int start){
    rep(i,1,start)
    a[i]=aa[i];
    lena = start;
    a[lena+1] = '#';
    rep(i,start+1,Len)
    a[i+1]=aa[i];
    n = Len+1;
    //cout << n<<a+1<<endl;
    for(int i = 1; i <= n ; i++){
        st[i] = make_pair(a[i],i);
    }

    sort(st+1,st+1+n);
    k = realrank[st[1].second] = 1;
    for(int i = 2; i <= n ; i++){
        if(st[i].first != st[i-1].first)
            k +=1;
        realrank[st[i].second] = k;
    }
}
void suffix_array(){
    for(int i = 1; i <= n ; i *= 2){
        for(int j = 0 ; j <= n ; j++)
            A[j] = B[j] = 0;
        for(int j = 1;  j <= n ; j++){
            A[s[j][0] = realrank[j]]++;
            if(j+i <= n)
                s[j][1] = realrank[j+i];
            else    s[j][1] = 0;
            B[s[j][1]]++;
        }
        for(int j = 1; j <= n ;j++)
            A[j] += A[j-1],B[j] += B[j-1];
        for(int j = n ; j >= 1; j--){
            C[B[s[j][1]]] = j;
            B[s[j][1]]--;
        }
        for(int j = n ; j >=1; j--){
            D[A[s[C[j]][0]]] = C[j];
            A[s[C[j]][0]]--;
        }
        k = realrank[D[1]] = 1;
        for(int j = 2 ; j <= n ; j++){
            if(s[D[j]][0] != s[D[j-1]][0] ||s[D[j]][1] != s[D[j-1]][1])
                k++;
            realrank[D[j]] = k;
        }
    }
}

void gethigh(){
    int pre = 0,j;
    for(int i = 1; i <= n ; i++){
        if(pre) pre--;
        j = D[realrank[i]-1];
        while(i+pre <= n && j + pre <= n && a[i+pre] == a[j+pre])
            pre++;
        h[realrank[i]] = pre;
    }
}
struct node{
    long long cnt,height;
}stka[maxn],stkb[maxn];

long long ans ,tot;
int topa ,topb,taila,tailb;

ll sov(){
    //cout <<"len = "<<lena<<"  "<<n<<endl;
    ans = topa = topb = taila = tailb = tot = 0;
    for(int i = 2; i <= n ; i++){
       // printf("h[%d] = %d\n",i,h[i]);
        if(h[i] < K){
            topa = topb = taila = tailb = 0;
            tot = 0;
        }
        else{
            int num = 0;
            if(D[i-1] <= lena){
                num++;
                tot += h[i] - K +1;
               // cout <<"i = "<<i<<"  to "<<tot<<endl;
            }
            while(taila > topa && stka[taila-1].height >= h[i]){
              //  cout <<"i = "<<i<<endl;
                taila--;
                tot -= stka[taila].cnt*(stka[taila].height-h[i]);
                num += stka[taila].cnt;
               //  cout << " tot =  "<<tot <<endl;
            }

            stka[taila].height = h[i];
            stka[taila].cnt = num;
            taila++;
            if(D[i] > lena){
                ans += tot;
            }
        }
    }

    topa = topb = taila = tailb = tot = 0;
    for(int i = 2; i <= n ; i++){
        if(h[i] < K){
            topa = topb = taila = tailb = 0;
            tot = 0;
        }
        else{
            int num = 0;
            if(D[i-1] > lena+1){
                //printf("D[%d] = %d\n",i,D[i]);
                num++;
                tot += h[i] - K +1;
            }
            while(taila > topa && stka[taila-1].height >= h[i]){

                taila--;
                tot -= stka[taila].cnt*(stka[taila].height-h[i]);

                num += stka[taila].cnt;
            }
            //cout <<"i = "<<i<<"   stka[taila].height = "<<h[i]<<"  stka[taila].cnt = "<<num<<endl;
            stka[taila].height = h[i];
            stka[taila].cnt = num;
            taila++;
            if(D[i] <= lena){
                ans += tot;
            }
        }
    }
    return ans;
}

int main(){
    K=1;
    int T;
    while(~scanf("%s",aa+1)){
        memset(anss,0,sizeof(anss));
        Len=strlen(aa+1);
        rep(i,1,Len-1)
        {
            init(i);
            suffix_array();
            gethigh();
            anss[i]=sov();
            //anss[i]>>=1;
            //cout<<anss[i]<<endl;
        }
        scanf("%d",&T);
        while(T--)
        {
            int x;
            scanf("%d",&x);
            printf("%lld\n",anss[x]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LSD20164388/article/details/89318550