ZYB玩字符串

比较巧妙的一个字符串区间DP。

很容易相当枚举n的约数以及左端点来确定答案,问题的重点在于如何判定是否能通过复制P来得到S。

首先可以想出一个很暴力的dp。
dp[l][r][a][b]表示l到r这个区间,除去整段的P外,剩下的字符对应P中的[a,b]是否可行。

dp[l][r][a][b]表示l到r这个区间,除去整段的P外,剩下的字符对应P中的[a,b]是否可行。
转移的时候枚举一个断点k,枚举一个断点位置的t表示左侧的结尾字符和右侧的起始字符。
判定的复杂度O(n^6)。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define N 50
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
char s[N],t[N];
int dp[N][N][N][N];
int main()
{
    int n,m,i,j,a,b,k,p;
    scanf("%s",s);n=strlen(s);for(i=n;i>=1;i--)s[i]=s[i-1];
    scanf("%s",t);m=strlen(t);for(i=m;i>=1;i--)t[i]=t[i-1];
    for(i=1;i<=n;i++)
      for(j=1;j<=m;j++)
      if(s[i]==t[j])dp[i][i][j][j]=1;
    for(i=n;i>=1;i--)
    for(j=i+1;j<=n;j++)
    for(a=1;a<=m;a++)
    for(b=a;b<=m;b++)
    { 
        for(k=i;k<=j-1;k++)
        {
            for(p=1;p<=m-1;p++)
            dp[i][j][a][b]|=(dp[i][k][a][p]&&dp[k+1][j][p+1][b]);
            dp[i][j][a][b]|=(dp[i][k][a][m]&&dp[k+1][j][1][b]);
            dp[i][j][a][b]|=(dp[i][k][a][b]&&dp[k+1][j][1][m]);
            dp[i][j][a][b]|=(dp[i][k][1][m]&&dp[k+1][j][a][b]);
            if(dp[i][j][a][b])break;
        }
    }
    printf("%d",dp[1][n][1][m]);
    return 0;
}

考虑怎么更优秀地判定是否合法
显然S可以写成类似以下这种形式

123PPP45PP67PPP8P

即由一个被分割的整段和数个可以被消掉的段组成

注意到这里有一个很明显的子问题的结构

考虑一个从左到右判定的dp
每一步有两种决策
1.顺着上次的零碎部分继续放
dp[i][j]---->dp[i][j+1] (s[j+1]==ch[(j-i+1)%m+1])//s[j+1]等于下一个零碎字符

2.放一个长度为m的倍数的合法的区间(子问题)
dp[i][j]---->dp[i][j+k*m]; (dp[j+1][j+k*m] is true)//为一个合法的区间

再魔改一下搞成区间dp的那种形式就好啦

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<cstdlib>
#include<algorithm>
#define N 330
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
const ll mo=inf;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
set<int>qwq;
char s[N],ch[N],ans[N];
int n,m,h[N],h_[N],dp[N][N];
int dfs(int l,int r)
{
    if(dp[l][r]!=-1)return dp[l][r];
    if(l==r)
    {
        dp[l][r]=s[l]==ch[1];
        return dp[l][r];
    }
    dp[l][r]=false;
    dp[l][r]|=dfs(l,r-1)&(s[r]==ch[(r-l)%m+1]);
    for(int k=1;k<=(r-l)/m;k++)
    dp[l][r]|=dfs(l,r-k*m)&dfs(r-k*m+1,r);
    return dp[l][r];
}
void work()
{
    int i,j;
    scanf("%s",s);n=strlen(s);
    qwq.clear();
    for(i=1;i<=26;i++)h[i]=0;
    for(i=n;i>=1;i--)s[i]=s[i-1],h[s[i]-'a'+1]++;
    for(i=1;i<=n;i++)ans[i]='z'+1;
    for(m=1;m<=n;m++)
    if(n%m==0)
    {
        for(i=1;i<=n-m+1;i++)
        {
            bool flag=true;
            int hash=0;
            for(j=1;j<=26;j++)h_[j]=0;
            for(j=i;j<=i+m-1;j++)
            {
                ch[j-i+1]=s[j];
                h_[s[j]-'a'+1]++;
                hash=((ll)hash*141ll+(ll)s[j])%mo;
            }
            if(qwq.count(hash))continue;
            qwq.insert(hash);
            for(j=1;j<=26;j++)if(h[j]!=h_[j]*(n/m))flag=false;
            if(!flag)continue;
            memset(dp,-1,sizeof(dp));
            if(dfs(1,n))
            {
                for(j=1;j<=m;j++)
                if(ch[j]!=ans[j])
                {
                    if(ch[j]<ans[j])
                    {
                        for(j=1;j<=m;j++)ans[j]=ch[j];
                        break;
                    }
                }
            }
        }
        if(ans[1]!='z'+1)
        {
            for(i=1;i<=m;i++)printf("%c",ans[i]);
            printf("\n");
            return;
        }
    }
    return;
}
int main()
{
    int t=read();
    for(int i=1;i<=t;i++)work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Creed-qwq/p/10015007.html