最长前缀——浅谈转移方程的寻找

IOI96】最长前缀

Description

一些生物体的复杂结构可以用其基元的序列表示,而一个基元用一个大写英文字符串表示。生物学家的一个问题就是一个这样的长序列分解为基元(字符串)的序列。对于给定的基元集合P,如果可以从中选出N个基元P1P2P3...,Pn,将它们各自对应的字符串依次连接后得到一个字符串S,称S可以由基元集合P构成。在从P中挑选基元时,一个基元可以使用多次,也可不用。例如,序列 ABABACABAAB 可以由基元集合{AABBACABBC} 构成。

字符串的前K个字符为该字符串的前缀,其长度为K。请写一个程序,对于输入的基元集合P和字符串T,求出一个可以由基元集合P构成的字符串T的前缀,要求该前缀的长度尽可能长,输出其长度。

Input

  输入数据的开头是基元集合P,包括 1..200 个元素(长度为 1..10 )组成的集合,用连续的以空格或者回车键分开的字符串表示。字母全部是大写,数据可能不止一行。元素集合结束的标志是一个只包含一个 “.” 的一行。集合中的元素没有重复。接着是大写字母序列T ,长度为 1..200,000 ,用一行或者多行的字符串来表示。换行符并不是序列T的一部分。

Output

只有一行,输出一个整数,表示基元集合P构成的字符串T的前缀的长度。

Sample Input

A AB BA CA BBC

 ABABACABAABC

Sample Output

  

状态转移方程:f[i]=f[i-len]+len

len是字典中单词的长度,f[i]表示到前i个字符的最长长度。

先前找的是这样一个方程:f[i]表示用字典中的单词,可否拼出长度为i的字符串。实际操作起来非常繁琐:

1.看第i个字符与它之前的字符(或者它本身)组成的单词是否为字典中的单词,如果是,则f[i]=1,否则执行2;

2.看第i个字符与它之后的字符组成的单词是否为字典中的单词,如果是,则f[i]=1,否则执行3;

3.看第i个字符与它之前、之后的字符共同组成的单词是否在字典中,如果是,则f[i]=1,否则,i-1为最终答案。

可以看到,程序需要考虑的细节非常之多,稍有不慎就会出错。而我也是犯了许多错误之后才能将思路理到如此地步。

可以说,好的转移方程会大大降低编程难度。但是不是所有动态规划都能找到一劳永逸的方程呢?我想不是吧。所以说,

关键是锻炼自己的思维,并做好编复杂程序的心里准备。

 

代码
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4  int a[11][201],sum[11]={0};
5  char b[201][10];
6  int f[200001]={0},n;
7  char list[200000],li[20000];
8
9  int check(int fr,int l)
10 {
11 int i,j,flag=1;
12 if(sum[l]==0)return 0;
13 for(j=1;j<=sum[l];j++)
14 {
15 flag=1;
16 for(i=0;i<l;i++)
17 {
18 if(list[i+fr]!=b[a[l][j]][i])
19 flag=0;
20 }
21 if(flag==1)break;
22 }
23 return flag;
24 }
25
26  int main(){
27 FILE *in,*out;
28 int i=1,j,i1=0,k,l,flag=0,max;
29 in=fopen("input2.txt","r");
30 out=fopen("output.txt","w");
31
32 fscanf(in,"%s",b[1]);
33 while(b[i][0]!='.')
34 {
35 l=strlen(b[i]);
36 a[l][++sum[l]]=i;
37 fscanf(in,"%s",b[++i]);
38 }
39 while(fscanf(in,"%s",li)!=EOF)
40 strcat(list,li);
41 n=strlen(list);
42
43 for(i=1;i<=n;i++)
44 {
45 for(k=1;k<=10;k++)
46 if(i>=k)
47 {
48 flag=check(i-k,k);
49 if(flag&&f[i-k]||flag&&i-k==0)
50 f[i]=k+f[i-k];
51 if(max<f[i])max=f[i];
52 }
53 }
54
55 fprintf(out,"%d\n",max);
56 fclose(in);
57 fclose(out);
58 return 0;
59 }
60  

 

check(fr,l)函数是用来检查题目给出的字符串中从fr到fr+l-1的范围之内的字符所组成的单词是否

在字典中,是则返回1.b[i]存储字典中第i个单词。a[i][j]存储长度为i的第j个字典中的单词,为的

是节省查找时间。最终答案是f[i]中的最大值。

其实程序难度本来不大,但是对具体单词起止位置的考虑花去了太多时间,究其原因是对位置不敏感,

又没采取直观方式去分析。下次分析此类问题,还是该画图啊。

 

  

posted @ 2010-08-03 18:02  Danty  阅读(515)  评论(0编辑  收藏  举报