• UVA 1401 Remember the Word


    题意:给出一个字符串和S个单词组成的字典,问把这个字符串分解成若干单词的连接,总共有多少种?(单词可重复)

    解析:这是刘汝佳白皮书的关于Trie树的入门,也是我接触的第一题。总的思路就是可以用递推来求到底有多少种分解方法。假如用d[i]表示从第i个字符开始往后的后缀字符串的分解方法,即s[i]->s[len]  (len为字符串长度)之间的字符串。

    那么假如说在s[i...len]的字符串中发现s[i...i+n]是一个字典中的单词,那么d[i]  = d[i] + d[i+n];同理的话d[i] = sum{ d[i+len(x)]  },x是s[i...len] 的一个前缀单词。

    那么我们不可能枚举所有的单词x来判断他是否是s[i...len]的一个前缀,那样必然超时,所以我们就把字典中的单词全部都存到Trie树中,在树中查找单词。


    关于Trie树,我们对所有的节点全都从1到n标上序号,其中0为根节点,那么总共有sz个节点。然后因为字符集只是小写字母,所以我们用ch[i][0] != 0表示节点i有个子节点是字符'a',ch[i][1] != 0表示节点i有个子节点为'b',ch[i][2] = 0表示节点i没有'c'的子节点.

    其中Trie树还有个附加信息,存在val[]中,如果该点是单词节点的话,val[]中存储的就是该点的该单词的长度,否则val[]为0



    #include"stdio.h"
    #include"string.h"
    #include"vector"
    using namespace std;
    #define maxnode 100*4000+10		//跟sz的大小相同
    #define sigma_size 27
    #define Mod 20071027
    
    vector<int>ans;		//用来存储字符串后缀中所存在单词的长度
    struct Trie{
    	int ch[maxnode][sigma_size];	//用来存储Trie树
    	int val[maxnode];				//树节点的附加信息
    	int sz;							//整棵树节点的总数
    	void init(){			
    		sz = 1;				//初始只有一个根节点,编号为0
    		memset(ch[0],0,sizeof(ch[0]));
    	}
    	int idx(char c){
    		return c - 'a';		//对于小写字母集,获得c的编号
    	}
    
    	//插入字符串s,v代表的是附加信息。如果v为0代表本节点不是单词节点,所以v必须非0
    	void insert(char *s,int v){
    		int u = 0,len = strlen(s);
    		for(int i = 0 ; i < len ; i ++){
    			int c = idx(s[i]);
    			if(!ch[u][c]){		//如果该节点不存在
    				memset(ch[sz],0,sizeof(ch[sz]));
    				val[sz] = 0;		//中间节点,不是单词节点,附加信息为0
    				ch[u][c] = sz++;	//新建节点
    			}
    			u = ch[u][c];	//到下一节点
    		}
    		val[u] = v;		//字符串最后一个必然是单词节点
    	}
    
    	//查询从指针s开始长度为len的字符串是否存在
    	void find(char *s , int len){
    		int u = 0;
    		//int len = strlen(s);	每次都求长度的话会超时,所以找规律
    		ans.clear();
    		for(int i = 0 ; i < len ; i++){
    			int c = idx(s[i]);
    			if(!ch[u][c])
    				return ;	//不存在该字符串
    			u = ch[u][c];
    			if(val[u])	//如果发现该节点是单词节点,就把该单词长度存入ans
    				ans.push_back(val[u]);
    		}
    	}
    }t;
    
    int d[300010];
    char str[300010],temp[110];
    int n;
    
    void solve(){
    	memset(d,0,sizeof(d));
    	int len = strlen(str);
    	d[len] = 1;			//给予一个初始值
    	for(int i = len - 1; i >= 0 ; i--){
    		t.find(str+i,len-i);
    		for(int j = 0 ; j < ans.size(); j++){
    			d[i] = (d[i]+d[i+ans[j]])%Mod;
    		}
    	}
    	printf("%d
    ",d[0]);
    }
    
    int main(){
    	memset(str,0,sizeof(str));
    	int Case = 1;
    	while(scanf("%s",str) != EOF){
    		scanf("%d",&n);
    		t.init();
    		for(int i = 0 ; i < n ; i ++){
    			scanf("%s",temp);
    			int len = strlen(temp);
    			t.insert(temp,len);	//单词节点储存的信息就是该单词的长度
    		}
    		printf("Case %d: ",Case++);
    		solve();
    	}
    	return 0;
    }


  • 相关阅读:
    HDU 2098 分拆素数和 数论
    CodeForces The Endless River
    CodeForces Good Words
    CodeForces A or B Equals C
    HDU 1251 统计难题 字典树/STL
    CSUOJ 1555 Inversion Sequence 线段树/STL
    OpenJudge P4979 海贼王之伟大航路 DFS
    敌兵布阵 线段树
    HDU 4004 The Frog's Games 二分
    HDU 2578 Dating with girls(1) 二分
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3295202.html
Copyright © 2020-2023  润新知