心胸决定格局,眼界决定境界...

Trie 和dp

#define MAX_WORD_CNT 4000
#define MAX_WORD_LEN 100
#define MAX_STRING_LEN  300000
int  d[MAX_STRING_LEN];


typedef struct Trie
{
	//生成字典树
    #define MAX_NODE 100
	#define MOD 20071027
	int nodes[MAX_NODE][26];//第i个结点的第j个孩子  的节点编号;
	int sz;//节点的总数
	int value[MAX_NODE];//节点权重,可以标注是否为单词的结尾
	//value[0] 没有意义

	Trie()
	{
		sz = 1;
		memset(nodes[0], 0, sizeof(nodes[0]));	   
		memset(value, 0, sizeof(value));
	}

	int get_index(char a){ return a - 'a'; }
	void insert(char* content, int weight)//插入字符串content
	{

		//节点总数从1开始.
		//
		int u = 0;//根节点从0开始
		int v = 0;
		int len = strlen(content);
		assert(len <= MAX_STRING_LEN);
		//memset(nodes, 0, sizeof(nodes));//错,如果再插入一个字符串,之前的nodes的编号信息全部丢失
		for (v = 0; v < len; v++)
		{
			char tmp = content[v];//遍历待插入字符串的各个字符
			int  idx = tmp - 'a';//对应的编号		
			if (!nodes[u][idx])//表示新节点   			
			{//刚开始是0,根节点,新插入的节点'a'不存在
				//memset(nodes[u], 0, sizeof(nodes[u]));//错,此处应该是第sz个节点,连接的信息都重置化
				memset(nodes[sz], 0, sizeof(nodes[sz]));//即将插入的第sz个节点
				value[sz] = 0;			
				nodes[u][idx] = sz;//当前插入的是第几个节点,或者说是节点编号
				sz++;//新添加的节点
				//u = nodes[u][idx];//指向的下一个节点  编号  错,如果这个节点存在呢,所以放的位置不对
			}
			u = nodes[u][idx];
		}
		value[u] = weight;
	}
	int find(const char* match)
	{
		int u = 0;
		int flag = 1;
		int len = strlen(match);
		for (int i = 0; i < len; i++)
		{
			int idx = get_index(match[i]);
			if (nodes[u][idx])
			{
				u = nodes[u][idx];//逐个字符去找
			}				
			else
			{
				flag = 0;
				break;
			}
				
		}
		if (!value[u])//不要忘记了,是否为某个单词的结尾
			flag = 0;

		return flag;	
	}

	void query(char* s, int state)
	{
		int u = 0; 
		int n = strlen(s);
		//"d"
		//"cd"
		//"bcd"
		//"abcd"
		int len = 0;
		for (int i = state; i < n; i++)//匹配是否存在匹配S[i...L]的前缀,
		{
			char c = s[i];//遍历s的字符,从state位置开始
			int  idx = get_index(c);//该字符对应的child编号
			if (!nodes[u][idx])//如果不存在该字符,则返回
				return;
			//如果存在,则指向下一个编号
			u = nodes[u][idx];
			len++;//len(x)
			if (value[u] && u)//是否该编号节点  为某个单词的结尾
				d[state] = (d[state] + d[state + len]) % MOD;
		}
	}

}Trie;


//单词拆分  

//词典集合  

//文本串

//将文本串拆分 为词典的多个单词拼接,单词可以重复使用

	//Trie树
	//Trie的精髓在于利用了公共前缀,生成一个关系树,一条边代表一个字符,节点的val值来记录是否为单词结尾;
	//Trie  test;
	//test.insert("abcdefg",  1);
	//test.insert("abab",  1);
	//test.insert("tede", 1);
	//test.insert("tea", 1);
	////find "teacher"
	//string sample("teacher");
	//cout << sample << ":" << test.find(sample.c_str()) << endl;
	////find "ted"
	//sample = "ted";
	//cout << sample << ":" << test.find(sample.c_str()) << endl;



	//单词拆分 
	//eg

	Trie  test;
	test.insert("a",  1);
	test.insert("b",  1);
	test.insert("ab", 1);
	test.insert("cd", 1);
	test.insert("abcd", 1);
	//文本串  abcd
	//d[i] 表示以第i个字符开始的后缀,即 S[i...L]的分解方案数
	//d[0]    后缀S[0...L]   "abcd"     字典单词匹配    "a",  "ab"    d[0] = d[0+1] + d[0+2] = 2
	//d[1]    后缀S[1...L]    "bcd"                   "b",          d[1] = d[1+1] = 1
	//d[2]    后缀S[2...L]     "cd"                   "cd"          d[2] = d[2+2] = 1
	//d[3]    后缀S[3...L]      "d"                    无
	//d[4] = 1
	//DP动态规划 d[3] ->d[2] ->d[1] ->d[0]

	//串 "abcd"
	char s[MAX_STRING_LEN] = { "abcd" };
	int n = strlen(s);
	memset(d, 0, sizeof(d));
	d[n] = 1;//结尾
	for (int i = n - 1; i >= 0; i--)//从文本字符串最后一个元素开始
	{
		test.query( s, i);
	}

	cout << "d[0]=" << d[0] << endl;

  

posted @ 2019-02-18 22:42  WELEN  阅读(188)  评论(0)    收藏  举报