GDOI 2016 Day1第二题 最长公共子串

Posted 无尽的蓝黄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GDOI 2016 Day1第二题 最长公共子串相关的知识,希望对你有一定的参考价值。

分析

首先,可以发现,区间是可以合并滴。把区间按左端点排序,对于两个区间[l1,r1]、[l2,r2],当l1<=l2 and r1>=l2,那么,将它们合成一个新的区间[l1,r2]。当一个位置不属于任何一个区间时,它自己独立成为一个区间。
接着dp,保证区间是从小到大的。
f[i][j]表示在从Si个区间,和子串T[j~|T|]的最长公共子串。
转移,
定义g[i]表示Si个区间的长度
枚举子串T[j~j+g[i]-1]每一个位置,当枚举到k时,T[j~k]T[k]的个数大于Si个区间中T[k]的个数,那么就breakf[i][j]=k-j+1。如果f[i][j]等于g[i],那么,f[i][j]=f[i][j]+f[i+1][j+g[i]]
但是,最长公共子串的串首并不一定是区间的串首,所以从i-1开始,往前搜,方法同上,设搜到的长度是num,ans就是max(num+f[i][j])。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
using namespace std;
int lens,lent,f[4110][4100],g[5000],sum[2100][30],st[2100],b[100002][3],n,m,tot,ans,sm[30];
char s[4100],t[4100];
void q(int l,int r)
{
    int i=l,j=r,mid=b[(l+r)/2][1],e;
    while(i<j)
    {
        while(b[i][1]<mid) i++;
        while(b[j][1]>mid) j--;
        if(i<=j)
        {
            e=b[i][1];
            b[i][1]=b[j][1];
            b[j][1]=e;
            e=b[i][2];
            b[i][2]=b[j][2];
            b[j][2]=e;
            i++;
            j--;
        }
    }
    if(i<r) q(i,r);
    if(l<j) q(l,j);
}
int main()
{
    scanf("%s\n%s\n",t,s);
    lent=strlen(t);
    lens=strlen(s);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&b[i][1],&b[i][2]);
    }
    for(int i=0;i<=lens-1;i++)
    {
        b[++n][1]=b[n][2]=i;
    }
    q(1,n);
    for(int i=1;i<=n;i++)
    {
        if(st[tot]+g[tot]-1<b[i][1])
        {
            st[++tot]=b[i][1];
            g[tot]=b[i][2]-b[i][1]+1;
        }
        else
        {
            g[tot]=max(g[tot],b[i][2]-st[tot]+1);
        }
    }
    for(int i=1;i<=tot;i++)
    {
        for(int j=st[i];j<=st[i]+g[i]-1;j++)
        {
            sum[i][s[j]-96]++;
        }
        sum[i][0]=maxlongint;
    }
    for(int i=tot;i>=1;i--)
    {
        for(int j=lent-1;j>=0;j--)
        {
            int bz=true,g1=0;
            memset(sm,0,sizeof(sm));
            for(int k=j;k-j+1<=g[i];k++)
            {
                if(t[k]==t[-1])
                {
                    
                }
                else
                if(k<=lent-1 && ++sm[t[k]-96]<=sum[i][t[k]-96])
                {
                    f[i][j]++;
                }
                else
                {
                    bz=false;
                    break;
                }
            }
            if(bz)
            {
                f[i][j]+=f[i+1][j+g[i]];
            }
            int num=0;
            memset(sm,0,sizeof(sm));
            for(int k=j-1;k+g[i-1]-1>=j;k--)
            {
                if(++sm[t[k]-96]<=sum[i-1][t[k]-96])
                {
                    num++;
                }
                else
                {
                    bz=false;
                    break;
                }
            }
            if(f[i][j]+num>ans)
            {
                ans=f[i][j]+num;
            }
        }
    }
    printf("%d",ans);
}



以上是关于GDOI 2016 Day1第二题 最长公共子串的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode算法第二题

GDOI2016模拟4.22总结

子序列与子串问题

子序列与子串问题

子序列与子串问题

noip2013提高组day1第二题火柴排队