您的位置:首页 > 其它

JZOJ5416. 【NOIP2017提高A组集训10.22】密码 DP

2017-10-23 17:29 399 查看
Description

现在身为校庆志愿者的小C正在引导校友们到他们集合的教室。终于,忙了一段时间的他可以休息一会儿了。这时,旁边一位老校友的话吸引到了他。“我后来当了一名探险家,有一次,我来到了一个地方,在正前方有一扇门,旁边写着一行文字:’现在给你前m个字符串G,有一个拼接规律T,它是一个长度为m的一个排列,你要把现在已经得到的最后m个字符串按照T的顺序拼接起来,得到一个新的字符串,用这种方法,你可得到第n个字符串P,再给你另外一个字符串S,则S在P中出现的次数就是这个门的密码…’”听到这里,小C陷入了沉思:到底密码是多少呢?不过由于小C比较讨厌大数,他只想知道这个密码除以(10^9+7)的余数。你能帮帮他吗?

Input

第一行包含一个整数m,表示开始字符串的个数(排列长度)。

接下来行,每行一个字符串,表示前m个字符串G,保证字符串的长度均相同。

接下来一行,包含m个正整数,表示T。

接下来一行,包含一个整数q,表示数据组数。

对于每一组数据,包含一个字符串和一个整数,分别表示S及n。

Output

输出总共行,每一个询问对应一个答案。

Sample Input

3

ab

ac

ca

2 3 1

3

ac 2

b 1

a 5

Sample Output

​1

1

5

Data Constraint

对于20%的数据,m<=2 ,|G|<=6 ,n<=15 。

对于30%的数据, m<=5 ,|G|<=50 ,n<=400 。

对于50%的数据,m<=5 ,|G|<=50 ,n<=20000 。

对于80%的数据, m<=9 ,|G|<=100 ,n<=10^6。

对于100%的数据, m<=9 ,|G|<=200 ,n<=10^9,q<=7,|S|<=|G|。

很鬼畜的题目,比赛的时候只会50,然后还因为传递地址等各种乱七八糟的c++语法然后挂了。。

80分应该是比较好大的,100分比较复杂我就放弃了,所以这里只讲80分的方法。

注意到n<=10^6,那么我们可以线性dp。

首先我们知道,如果要把m个连起来,那么他们的贡献就是原来每一块的贡献和连接处的贡献,然后线性推出f[i]。

先把每一块的贡献预处理出来直接算进f里面,然后连接处的贡献的话就设suf[i][j],pre[i][j],pre[i,j]表示第i个字符串的前j个字符和s的后j个字符能否匹配,suf[i][j]表示第i的字符串的后j个字符串和s的前j个字符串能否匹配,然后在预处理一些东西:

预处理link[i,j]表示把第i个串接到第j个串前面会多出现多少次s

记录form[i,0/1]分别表示第i个串前面/后面是哪个字符串

然后直接转移即可。

具体的话就是

f[i]+=link[from[i−m−1+p[j]][1]][from[i−m−1+p[j+1]][0]]

很好理解啦。

ymw真的强QAQ。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2e4+5;
const int mo=1e9+7;
int n,m;
int f[N],w[20],link[20][20];
int p[20],len,from[N][2];
bool suf[205][205],pre[205][205];
char ch[205],s[20][205];
inline int solve()
{
int l=strlen(ch+1);
fo(i,1,m)
{
w[i]=0;
fo(j,1,len-l+1)
{
int flag=1;
fo(k,1,l)
if (ch[k]!=s[i][j+k-1])
{
flag=0;
break;
}
w[i]+=flag;
}
fo(j,1,l)
{
int flag=1;
pre[i][j]=suf[i][j]=0;
fo(k,1,j)
if (ch[l-j+k]!=s[i][k])
{
flag=0;
break;
}
pre[i][j]=flag,flag=1;
fo(k,1,j)
if (ch[j-k+1]!=s[i][len-k+1])
{
flag=0;
break;
}
suf[i][j]=flag;
}
}
fo(i,1,m)
fo(j,1,m)
{
link[i][j]=0;
fo(k,1,len-1)
if (suf[i][k]&&pre[j][l-k])link[i][j]++;
}
fo(i,1,m)f[i]=w[i],from[i][0]=from[i][1]=i;
fo(i,m+1,n)
{
f[i]=0;
int tmp=i-m-1;
fo(j,1,m)f[i]+=f[i-j],f[i]%=mo;
fo(j,1,m-1)
f[i]+=link[from[tmp+p[j]][1]][from[tmp+p[j+1]][0]],
f[i]%=mo;
from[i][0]=from[tmp+p[1]][0];
from[i][1]=from[tmp+p[m]][1];
}
if (f
==0)f
=21;
return f
;

}
int main()
{
freopen("password.in","r",stdin);
freopen("password.out","w",stdout);
scanf("%d",&m);
fo(i,1,m)scanf("%s",s[i]+1);
len=strlen(s[1]+1);
fo(i,1,m)scanf("%d",&p[i]);
int q;
scanf("%d",&q);
while (q--)
{
scanf("%s%d",ch+1,&n);
printf("%d\n",solve());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: