标签:区间dp cpp 枚举 memset \n print cstring == for
比较巧妙的一个字符串区间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;
}
标签:区间dp cpp 枚举 memset \n print cstring == for
原文地址:https://www.cnblogs.com/Creed-qwq/p/10015007.html