codeforces 822 E. Liar(后缀数组+dp,好题)

题目链接

E. Liar
time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
The first semester ended. You know, after the end of the first semester the holidays begin. On holidays Noora decided to return to Vičkopolis. As a modest souvenir for Leha, she brought a sausage of length m from Pavlopolis. Everyone knows that any sausage can be represented as a string of lowercase English letters, the length of which is equal to the length of the sausage.

Leha was very pleased with the gift and immediately ate the sausage. But then he realized that it was a quite tactless act, because the sausage was a souvenir! So the hacker immediately went to the butcher shop. Unfortunately, there was only another sausage of length n in the shop. However Leha was not upset and bought this sausage. After coming home, he decided to cut the purchased sausage into several pieces and number the pieces starting from 1 from left to right. Then he wants to select several pieces and glue them together so that the obtained sausage is equal to the sausage that Noora gave. But the hacker can glue two pieces together only when the number of the left piece is less than the number of the right piece. Besides he knows that if he glues more than x pieces, Noora will notice that he has falsified souvenir sausage and will be very upset. Of course Leha doesn’t want to upset the girl. The hacker asks you to find out whether he is able to cut the sausage he bought, and then glue some of the pieces so that Noora doesn't notice anything.

Formally, you are given two strings s and t. The length of the string s is n, the length of the string t is m. It is required to select several pairwise non-intersecting substrings from s, so that their concatenation in the same order as these substrings appear in s, is equal to the string t. Denote by f(s, t) the minimal number of substrings to be chosen so that their concatenation is equal to the string t. If it is impossible to choose such substrings, then f(s, t) = ∞. Leha really wants to know whether it’s true that f(s, t) ≤ x.

Input
The first line contains single integer n (1 ≤ n ≤ 105) — length of sausage bought by Leha, i.e. the length of the string s.

The second line contains string s of the length n consisting of lowercase English letters.

The third line contains single integer m (1 ≤ m ≤ n) — length of sausage bought by Noora, i.e. the length of the string t.

The fourth line contains string t of the length m consisting of lowercase English letters.

The fifth line contains single integer x (1 ≤ x ≤ 30) — the maximum number of pieces of sausage that Leha can glue so that Noora doesn’t notice anything.

Output
In the only line print "YES" (without quotes), if Leha is able to succeed in creating new sausage so that Noora doesn't notice anything. Otherwise print "NO" (without quotes).

Examples
input
9
hloyaygrt
6
loyyrt
3
output
YES
input
9
hloyaygrt
6
loyyrt
2
output
NO
Note
Let's consider the first sample.

In the optimal answer, Leha should cut the sausage he bought in the following way: hloyaygrt = h + loy + a + y + g + rt. Then he numbers received parts from 1 to 6:

h — number 1
loy — number 2
a — number 3
y — number 4
g — number 5
rt — number 6
Hereupon the hacker should glue the parts with numbers 2, 4 and 6 and get sausage loyygrt equal to one that is given by Noora. Thus, he will have to glue three pieces. Since x = 3 you should print "YES" (without quotes).

In the second sample both sausages coincide with sausages from the first sample. However since x = 2 you should print "NO" (without quotes).

题意:给你一个长度为 \(n\) 的字符串 \(s1\),和一段长度啊为 \(m\) 的字符串 \(s2\),问你能否将 \(s1\) 切分成几块,选出不超过 \(x\) 块按照在原串 \(s1\) 中的顺序拼出 \(s2\)

题解:用 \(dp[i][j]\) 表示在 \(s1\) 中前缀 \(i\) ,选出 \(j\) 块能拼成的 \(s2\) 的最长前缀,那么有

\(dp[i][j]=max(dp[i][j],dp[i-1][j]);\)

\(dp[i+lcp][j+1]=max(dp[i+lcp][j+1],dp[i][j]+lcp);\)

这个 \(dp\) 有点不同,枚举到 \(dp[i][j]\) 时可以得到 \(lcp\),进而向后更新\(dp[i+lcp][j+1]\).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=2e5+100;


/*
 *suffix array
 *倍增算法  O(n*logn)
 *待排序数组长度为n,放在0~n-1中,在最后面补一个0
 *build_sa( ,n+1,m+1); //注意是n+1,m是s数组中的最大值;
 *getHeight(,n);
 *例如:
 
 *n   = 8;
 *num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
 *rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
 *sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
 *height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
 *
 */

int sa[maxn];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[maxn],t2[maxn],c[maxn];//求SA数组需要的中间变量,不需要赋值
int rk[maxn],height[maxn];
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*y=t2;
    //第一轮基数排序,如果s的最大值很大,可改为快速排序
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[i]]++;
    for(i=1;i<m;i++)c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        //直接利用sa数组排序第二关键字
        for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        //这样数组y保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;//下次基数排序的最大值
    }
}
void getHeight(int s[],int n)
{
    int i,j,k=0;
    for(i=0;i<=n;i++) rk[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)k--;
        j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}

int d[maxn][19];
void init_RMQ(int n)
{
    for(int i=1;i<=n;i++) d[i][0]=height[i];
    for(int k=1;(1<<k)<=n;k++)
        for(int i=0;i+(1<<k)<=n;i++)
            d[i][k]=min(d[i][k-1],d[i+(1<<(k-1))][k-1]);
}
int query(int l,int r)
{
    int k=0;
    while(1<<(k+1)<=r-l+1) k++;
    return min(d[l][k],d[r-(1<<k)+1][k]);
}
int lcp(int l,int r) //后缀l和后缀r
{
    int l1=rk[l],r1=rk[r];
    if(l1>r1) swap(l1,r1),swap(l,r);
    if(l1+1==r1) return height[r1];
    else return query(l1+1,r1);
}


char s[maxn];
int a[maxn];
int dp[maxn][32];
int main()
{
    int n,m,x;
    scanf("%d",&n);
    scanf("%s",s);
    rep(i,0,n) a[i]=s[i]-'a'+2;
    a[n]=1;
    scanf("%d",&m);
    scanf("%s",s);
    rep(i,0,m) a[n+i+1]=s[i]-'a'+2;
    int l=n+m+1;
    a[l]=0;
    scanf("%d",&x);
    build_sa(a,l+1,60);
    getHeight(a,l);
    init_RMQ(l);
    
    rep(i,0,n+1) rep(j,0,x+1)
    {
        if(i)
        dp[i][j]=max(dp[i][j],dp[i-1][j]);
        if(j!=x)
        {
            int t=lcp(i,n+1+dp[i][j]);
            dp[i+t][j+1]=max(dp[i+t][j+1],dp[i][j]+t);
        }
        
    }
    rep(j,0,x+1) if(dp[n][j]>=m) return 0*puts("YES");
    puts("NO");
    return 0;
}

posted @ 2017-08-30 21:57  tarjan's  阅读(317)  评论(0编辑  收藏  举报