USACO The Longest Prefix

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.


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值