dp[i][j]:i到j的串有多少种编码,求和减重复可得答案
dp1[i][j]:以i结尾和以j结尾最长相同的长度,用于处理重复
每次加上以当前j结尾的所有dp,枚举重复结尾求得最长重复长度,减去,前缀数字优化一下。
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int m;
char ch[3005];
long long dp[3005][3005];
char a[5];
long long mod=1e9+7;
int dp1[3005][3005];
bool check()
{
if(a[0]=='0'&&a[1]=='0'&&a[2]=='1'&&a[3]=='1')
return false;
if(a[0]=='0'&&a[1]=='1'&&a[2]=='0'&&a[3]=='1')
return false;
if(a[0]=='1'&&a[1]=='1'&&a[2]=='1'&&a[3]=='0')
return false;
if(a[0]=='1'&&a[1]=='1'&&a[2]=='1'&&a[3]=='1')
return false;
return true;
}
int main() {
while(~scanf("%d",&m))
{
for(int i=1;i<=m;i++)
getchar(),scanf("%c",&ch[i]);
memset(dp,0,sizeof(dp));
memset(dp1,0,sizeof(dp1));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
if(ch[i]==ch[j])
dp1[i][j]=dp1[i-1][j-1]+1;
int id;
for(int l=1;l<=3;l++)
{
id=j-l;
if(id>=i)
dp[i][j]+=dp[i][id];
else if(id==i-1)
dp[i][j]+=1;
}
id=j-4;
if(id>=i-1)
{
for(int k=0;k<4;k++)
{
a[k]=ch[id+1+k];
}
if(check())
{
if(id>=i)
dp[i][j]+=dp[i][id];
else if(id==i-1)
dp[i][j]+=1;
}
}
dp[i][j]%=mod;
}
}
for(int i=1;i<=m;i++)
for(int j=1;j<=i;j++)
{
dp[j][i]+=dp[j-1][i];
dp[j][i]%=mod;
}
long long tans=0;
for(int j=1;j<=m;j++)
{
long long ans=dp[j][j];
int tp=0;
for(int i=1;i<j;i++)
{
tp=max(tp,dp1[i][j]);
}
long long anss=dp[j][j]-dp[j-tp][j];
ans-=anss;
ans=(ans%mod+mod)%mod;
tans=(tans+ans)%mod;
printf("%lld\n",tans);
}
}
return 0;
}