USACO最长前缀(trie练习题)

点我

题目描述

在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的。生物学家对于把长的序列分解成较短的序列(即元素)很感兴趣。

如果一个集合 P 中的元素可以通过串联(元素可以重复使用,相当于 Pascal 中的 “+” 运算符)组成一个序列 S ,那么我们认为序列 S 可以分解为 P 中的元素。元素不一定要全部出现(如下例中BBC就没有出现)。举个例子,序列 ABABACABAAB 可以分解为下面集合中的元素:

{A, AB, BA, CA, BBC}

序列 S 的前面 K 个字符称作 S 中长度为 K 的前缀。设计一个程序,输入一个元素集合以及一个大写字母序列 S ,设S'是序列S的最长前缀,使其可以分解为给出的集合P中的元素,求S'的长度K。

输入输出格式

输入格式:

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

输出格式:

只有一行,输出一个整数,表示 S 符合条件的前缀的最大长度。

输入输出样例

输入样例#1:
A AB BA CA BBC
.
ABABACABAABC
输出样例#1:
11

说明

翻译来自NOCOW

USACO 2.3

代码

#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<ctime>
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Rep(i,a,b) for(register int i=a;i>=b;--i)
const int maxx=1e5+7;
using namespace std;
int be[2000001],ne[2000001],to[2000001],e=0,cnt=0;
void add(int x, int y){			//链式前向星存trie
    to[++e]=y;
    ne[e]=be[x];
    be[x]=e;
}
struct node{
    bool end; char x;
}llz[2000001];
bool f[2000001];
char x[1200],s[2000010];
void insert(char *s){				//trie的插入
    int i,u=0,len=strlen(s);
    For(v,0,len-1){
        bool flag=0;
        for(i=be[u];i;i=ne[i]){
            int go=to[i];
            if(llz[go].x==s[v]){
                u=go;
                flag=1;
                break;
            }
        }
        if(!flag) {add(u,++cnt); u=cnt; llz[u].x=s[v];}		//
        if(v==len-1) llz[u].end=1;				//标记结束位置
    }
}
bool find(int l,int r){						//trie的查询
    int i,u=0,flag;
    For(v,l,r){
        flag=0;
        for(i=be[u];i;i=ne[i]){
            int go=to[i];
            if(llz[go].x==s[v]){
                u=go;
                flag=1;
                break;
            }
        }
        if(!flag)return 0;
        if(v==r)
            if(llz[u].end==1){
                return 1;
            }
            else return 0;
    }
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
    freopen("output.out", "w", stdout);
#endif
    int i,j,l,ans=0,M=0;
    while(1){						//对每个单词集合建trie
        scanf("%s",x);
        if(x[0]=='.') break;
		i=strlen(x);
		M=max(i,M);					//找到最长的长度,下面会有用的
        insert(x);
    }
    scanf("%s",s+1);
    while(scanf("%s",x)!=EOF) strcat(s+1,x);		//读入s串
    l=strlen(s+1);
    f[0]=1;							//f[x]表示在x位置之前所有的字母能匹配
    For(i,1,l){
        for(j=i-1;j>=0;j--)
            if(f[j] && find(j+1,i)){	//如果j以前能全部匹配,并且j+1到i能匹配,i位置也能全匹配
                f[i]=1;
                ans=i;
                break;
            }
		if(j<0 && i-ans>M) break;    //剪枝,如果当前比最长集合还要长的位置没匹配成功,后面也不能匹配上
        }
    printf("%d\n",ans);
    return 0;
}


code by  罗旅洲

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
USACO (美国计算机奥林匹克竞赛)是一个广受欢迎的竞技性计算机编程比赛,对于新手来说思维题是一个很好的起点。思维题旨在培养学生的创造力、逻辑思维和问题解决能力。 对于USACO的新手思维题来说,我认为有几个重要的方面需要考虑。首先是理解题目,要准确地理解题目的要求和限制条件,包括输入输出格式、数据范围等。这样可以避免在解题过程中产生偏差。 其次是分析问题,要尽可能地通过例子和测试样例来观察和揭示问题的规律和特点。通过发现并解决子问题,逐步推导出正确的解决方法。可以使用逻辑推理、数学推导等工具辅助分析问题。 第三点是编程实现,根据经验和理解,选择合适的编程语言和算法进行实现。在代码编写过程中,注重代码的可读性和简洁性,合理利用变量和函数,避免重复代码和冗余操作。合理使用循环、条件判断等结构,确保程序的正确性和高效性。 最后是测试和调试,通过输入不同的测试样例来验证程序的正确性。特别是从边界条件和极端情况考虑,检查是否有错误或潜在的漏洞。当发现问题时,要运用调试工具和技巧来找出错误的原因,并对代码进行相应的修改和优化。 总之,USACO新手思维题是一个很好的锻炼编程思维和能力的机会。通过认真理解题目、分析问题、编写实现和测试调试,可以有效地解决这些思维题,提升自己的编程水平。随着不断的练习和积累,我相信每个新手都能够在USACO竞赛中进一步成长和取得更好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值