1、这是98年的一道IOI题目,自己花了两个小时左右做出来的,没看题解,好有成就感啊~
2、首先这道题可以暴搜,但是前几道题的经验告诉我这样是不可取的,于是考虑DP解法,发现DP其实就是“递推”,从初状态到末状态一层一层扫描、覆盖。。。DP最大的优势就是避免计算大量重复子问题,这也是我们省去的时间的来源。
3、用了栈保存前一个状态,一个省时的方法就是判重,也就是算过的dp不用再算,dp【i】==true代表前i个字符可以表示出来。
4、刚开始做错了,调试了好久,最后发现错误原因是光排了字符串,没把字符串的长度一起调换。下次要注意:排序的时候,其他参数是不是也一起调换了?
5、一共交了3次,还算不错吧。
/*
ID:mrxy564
PROG:prefix
LANG:C++
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
using namespace std;
stack<int> a;
bool dp[200010];
int cnt,ch,len;
char s[210][12],str[200010],L[210];
int cmp_string(const void *_a,const void *_b){
char*a=(char*)_a;
char*b=(char*)_b;
return strcmp(a,b);
}
void init(){
cnt=0;
while(scanf("%s",s[cnt])==1){
if(s[cnt][0]=='.') break;
else
cnt++;
}
qsort(s,cnt,sizeof(s[0]),cmp_string);
for(int i=0;i<cnt;i++)
L[i]=strlen(s[i]);
getchar();len=0;
while((ch=getchar())!=EOF){
if(ch!='\n')
str[len++]=ch;
}
}
bool Equal(char*a,char*b,int len){
for(int i=0;i<len;i++)
if(a[i]!=b[i]) return false;
return true;
}
int main(){
freopen("prefix.in","r",stdin);
freopen("prefix.out","w",stdout);
memset(dp,false,sizeof(dp));
init();
while(!a.empty()) a.pop();
for(int i=0;i<cnt;i++){
if(Equal(s[i],str,L[i])){
a.push(L[i]);
dp[L[i]]=true;
}
}
while(!a.empty()){
int index=a.top();
a.pop();
if(index>=len) continue;
for(int i=0;i<cnt;i++)
if(!dp[index+L[i]] && Equal(s[i],str+index,L[i])){
a.push(index+L[i]);
dp[index+L[i]]=true;
}
}
bool flag=false;
for(int i=len;i>=1;i--){
if(dp[i]==true){
printf("%d\n",i);
flag=true;
break;
}
}
if(!flag) printf("0\n");
return 0;
}
官方题解:
The basic tool to use here is dynamic programming. For each prefix of the sequence, you know that it is expressible in terms of the primitives if and only if you can find some shorter expressible prefix such that the sequence between the two prefixes is a primitive. Thus, you can check each primitive and see if it matches the end of a prefix and whether the prefix before that primitive (the pre-prefix, if you would) is expressible.
This can be turned-around to get something that is simpler to code. For each prefix that is expressible, determine which primitives match the sequence beginning at that location, and mark the prefix plus the primitive as expressible, if it does.
In the worst case, you have to check each prefix starting at each location. There are at most 200 prefixes of length 10 to check at each of 200,000 locations, for a total of 400,000,000 operations. This is a little higher than the standard number you would expect to be able to do in 5 seconds, but it is a very small operation (two array look-ups and a character comparison), and an over-estimate on how bad it can actually get.
#include <stdio.h> /* maximum number of primitives */ #define MAXP 200 /* maximum length of a primitive */ #define MAXL 10 char prim[MAXP+1][MAXL+1]; /* primitives */ int nump; /* number of primitives */ int start[200001]; /* is this prefix of the sequence expressible? */ char data[200000]; /* the sequence */ int ndata; /* length of the sequence */ int main(int argc, char **argv) { FILE *fout, *fin; int best; int lv, lv2, lv3; if ((fin = fopen("prim.in", "r")) == NULL) { perror ("fopen fin"); exit(1); } if ((fout = fopen("prim.out", "w")) == NULL) { perror ("fopen fout"); exit(1); } /* read in primitives */ while (1) { fscanf (fin, "%s", prim[nump]); if (prim[nump][0] != '.') nump++; else break; } /* read in string, one line at a time */ ndata = 0; while (fscanf (fin, "%s", data+ndata) == 1) ndata += strlen(data+ndata); start[0] = 1; best = 0; for (lv = 0; lv < ndata; lv++) if (start[lv]) { /* for each expressible prefix */ best = lv; /* we found a longer expressible prefix! */ /* for each primitive, determine the the sequence starting at this location matches it */ for (lv2 = 0; lv2 < nump; lv2++) { for (lv3 = 0; lv + lv3 < ndata && prim[lv2][lv3] && prim[lv2][lv3] == data[lv+lv3]; lv3++) ; if (!prim[lv2][lv3]) /* it matched! */ start[lv + lv3] = 1; /* so the expanded prefix is also expressive */ } } /* see if the entire sequence is expressible */ if (start[ndata]) best = ndata; fprintf (fout, "%i\n", best); return 0; }
Additional Analysis from Marian Dvorsky in Slovakia
After the main idea is clear, we can think about an improvements. First of all we notice that a trick can reduce space complexity from O(n) (where n is 200,000 - length of sequence) to O(maxl) (where maxl is 10, the length of the longest word in dictionary), but that requires an "opposite" method to the one Hal used in his program. For all prefixes of length n (n grows in increasing order), we check if they are expressible in terms of primitives.
The second improvement is the check for whether the end (suffix) of prefix is primitive. This can be also done in O(maxl) with a structure called a `trie'. We will have a tree in which every node represents some word (or prefix of some word) in a dictionary and edges represent characters. The root of the tree represents an empty word.
If we walk from root to some node `i' and write down characters on edges we've used, we get some word `v'. The node `i' then represents word `v'. Moreover if word `v' is in dictionary, we will remember it in that node (with some boolean variable).
Finally, when we learn that the last maxl prefixes aren't expressible, then no more prefixes are, so we can exit immediately.
In the worst case, we will, for every prefix (200,000), check a word of length maxl (10), for a total of 2,000,000 `operations'.
(* maximum length of primitive *) const MAXL=20; type pnode=^node; node=record next:array['A'..'Z'] of pnode; isthere:boolean; end; var trie:pnode; (* dictionary *) p:pointer; fin,fout:text; (* read space separated word from file *) function readword:string; var s:string; ch:char; begin read(fin,ch); while (not (ch in ['A'..'Z','.'])) do read(fin,ch); s:=''; while (ch in ['A'..'Z','.']) do begin s:=s+ch; read(fin,ch); end; readword:=s; end; (* read dictionary *) procedure readdict; var n,i,j,l:integer; nod:pnode; ch,ch2:char; s:string; begin new(trie); for ch2:='A' to 'Z' do trie^.next[ch2]:=nil; trie^.isthere:=false; s:=readword; while s<>'.' do begin nod:=trie; l:=length(s); (* save word in reverse order *) for j:=l downto 1 do begin ch:=s[j]; if nod^.next[ch]=nil then begin new(nod^.next[ch]); for ch2:='A' to 'Z' do nod^.next[ch]^.next[ch2]:=nil; nod^.next[ch]^.isthere:=false; end; nod:=nod^.next[ch]; end; nod^.isthere:=true; s:=readword; end; end; procedure compute; var start:array[0..MAXL] of boolean; (* is this prefix (mod MAXL) expressible ? *) data:array[0..MAXL] of char; (* the sequence (mod MAXL) *) i,k:integer; ch:char; nod:pnode; max,cnt:longint; begin start[0]:=true; read(fin,ch); data[0]:=#0; (* sentinel *) i:=1; max:=0; cnt:=1; while not eof(fin) do begin if not (ch in ['A'..'Z']) then begin read(fin,ch); continue; end; if i=MAXL then i:=0; (* i:=i mod MAXL *) k:=i; data[i]:=ch; start[i]:=false; nod:=trie; (* is there primitive at the and of current prefix ? *) while nod<>nil do begin nod:=nod^.next[data[k]]; dec(k); if k<0 then k:=MAXL-1; if start[k] and (nod<>nil) and nod^.isthere then begin (* we've found a primitive at the end of prefix and the rest is expressible *) start[i]:=true; max:=cnt; break; end; if data[k]=#0 then break; end; read(fin,ch); (* if last MAXL prefixes are not expressible, no other prefix can be expressible *) if cnt>max+MAXL then break; inc(i); inc(cnt); end; (* write answer *) assign(fout,'prefix.out'); rewrite(fout); writeln(fout,max); close(fout); end; begin mark(p); assign(fin,'prefix.in'); reset(fin); readdict; compute; close(fin); release(p); end.