EOJ:YO!
YO!
Time Limit: 1000MS | Memory Limit: 65536K |
Description
Given a starting word and a dictionary, determine how many ways you could "paint over" combinations of letters in the starting word and end up with one or more correctly spelled words. If a "paint over" combination produces multiple words (including the same word appearing at multiple locations in the starting word), then it's counted separately from the combinations that produce individual words. Multiple words need not be separated, and a single word may contain embedded spaces. For example, if the dictionary contains "he" and "vet" and the starting word is "CHEVROLET", then four possible combinations can be produced:
.HE......
.H.....E.
...V...ET
.HEV...ET
However, multiple words may not overlap each other in any way. For example, the starting word "CHEVROLET" can form individual words "hoe" and "vet", but the combination of both words would not be valid.
Input
Note that the starting word will always be in upper case, while the words in the dictionary may be mixed case. Neither the starting word nor the words in the dictionary will contain any embedded spaces, and the dictionary will not contain any duplicate words. And no, the dictionary is not guaranteed to be in any particular order.
Output
Sample Input
2
2 TOYOTA
toy
yo
5 CHEVROLET
does
he
like
her
program
Sample Output
2
3
原题地址:http://www.cn210.com/onlinejudge/problemshow.php?pro_id=253
____________________________________________________________________________________________
题解:
比赛中完全没有思路。
请教了发现这题居然是DP!
一看也是,明显的区间DP……然后就在那想方程。
1、首先想的第一个方程:DP[I][J]表示i到j区间里的总数,即要求的答案。DP[I][J]+=DP[I][K]*DP[K+1][J]。但是,假设1到2区间里恰有一个,那么1到3也会把这个算进去,1到4也会算进去,那么后面做乘法的时候就重复算了好多次……
————
2、于是改成了第二个方程,DP[I][J]表示恰好子序列的头在第I个,尾在第J个的方案总数。DP[I][J]=DP[I][K]*DP[L][J](L>K),但是发现这样也会重复计算:假设1到3恰有一个,4到5恰有一个,6到7恰有一个,那么DP[1][7]=DP[1][3]*DP[4][7]+DP[1][5]*DP[6][7],而实际上DP[1][3]*DP[4][7]和DP[1][5]*DP[6][7]是一样的。
————
3、无奈又去请教了大牛,得到大牛的方程:DP[I][J]表示区间里方案总数,即要求的答案。PRE[I][J]为预处理数组,即恰有从I到J的一个完整序列的个数。DP[I][J]=PRE[I][J]+DP[I+1][J]+PRE[I][K]*DP[K+1][J]【DP[I+1][J]就表示从I开始,空了一小段的情况。PRE[I][K]*DP[K+1][J]表示那段不空的情况】
说实话,我也不是太理解……
——
4、不过经过大牛的启发,我改进了我的转移方程
DP[I][J]表示恰好子序列的头在第I个,尾在第J个的方案总数。DP[I][J]=PRE[I][K]*DP[L][J](L>K).
1 #include<stdio.h>
2 #include<string.h>
3 int dp[31][31],flag[31][31],pre[31][31];
4 char dict[31],str[31];
5 int i,j,len,l,t,n,ans,temp;
6 void find(int start,int strl,int dictl)
7 {
8 int i;
9 if (len-strl<l-dictl) return;
10 if (dictl==l)
11 pre[start][strl-1]++;
12 for (i=strl;i<len;i++)
13 if (str[i]==dict[dictl])
14 find(start,i+1,dictl+1);
15 }
16 void work(int l,int r)
17 {
18 int i,j;
19 if (flag[l][r]!=0) return;
20 for (i=l;i<r;i++)
21 for (j=i+1;j<=r;j++)
22 {
23 work(l,i);
24 work(j,r);
25 dp[l][r]+=pre[l][i]*dp[j][r];
26 }
27 dp[l][r]+=pre[l][r];
28 flag[l][r]=1;
29 }
30 int main()
31 {
32 scanf("%d",&t);
33 while (t--)
34 {
35 memset(dp,0,sizeof(dp));
36 memset(pre,0,sizeof(pre));
37 memset(flag,0,sizeof(flag));
38 scanf("%d %s",&n,str);
39 temp=0;
40 len=strlen(str);
41 for (i=0;i<len;i++)
42 if (str[i]>='a'&&str[i]<='z')
43 str[i]=str[i]-'a'+'A';
44 getchar();
45 for (i=1;i<=n;i++)
46 {
47 gets(dict);
48 l=strlen(dict);
49 for (j=0;j<l;j++)
50 if (dict[j]>='a'&&dict[j]<='z')
51 dict[j]=dict[j]-'a'+'A';
52 if (strcmp(dict,str)==0) temp=1;
53 for (j=0;j<len;j++)
54 if (str[j]==dict[0])
55 find(j,j+1,1);
56 }
57 work(0,len-1);
58 ans=0;
59 for (i=0;i<len;i++)
60 for (j=i;j<len;j++)
61 ans+=dp[i][j];
62 printf("%d\n",ans-temp);
63 }
64 return 0;
65 }