• 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;
    }


  • 相关阅读:
    eclipse中文乱码问题解决方案
    修改Tomcat的JDK目录
    Tomcat 5.5 修改服务器的侦听端口
    HTML DOM教程 27HTML DOM Button 对象
    HTML DOM教程 24HTML DOM Frameset 对象
    Navicat for MySQL v8.0.27 的注册码
    HTML DOM教程 25HTML DOM IFrame 对象
    Tomcat 5.5 的下载和安装
    android manifest相关属性
    ubuntu10.04 下 eclipse 小结
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3295202.html
Copyright © 2020-2023  润新知