EOJ:YO!

YO!

Time Limit: 1000MS Memory Limit: 65536K

Description

While sitting in traffic on I-35 one day, you look up at the pickup in front of you and notice the word "YO" staring back at you from the tailgate. After a bit of closer investigation, you realize that the witty driver of the truck is driving a Toyota and has simply painted over the "TO" and "YA". As the traffic continues to sit, you beging to daydream and wonder how well this technique would work for other words.

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

The input will begin with a line containing a single integer N (1 ≤ N ≤ 100) indicating the number of data sets. Each data set will begin with a line of the form "X S". The integer X (1 ≤ X ≤ 200000) indicates the number of words in the dictionary for this data set. The string S specifies the starting word and will contain at least 1 character but no more than 30. The next X lines will be the dictionary for the data set and will each contain one word that is also at least 1 and at most 30 characters long.

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

For each data set in the input, output a line containing a single integer representing how many ways you could "paint over" combinations of letters in the starting word and end up with one or more correctly spelled words. At least one letter in the starting word has to be painted over to count; if the dictionary contains the same exact word as the starting word then, then this word would not be counted. You may assume that the total number of combinations per data set will not exceed 300000.

 

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 }

 

posted on 2010-08-07 16:18  风也轻云也淡  阅读(163)  评论(0编辑  收藏  举报