poj 3630 trie树 基本使用

题意:

给定一个电话号码本,看看有没有哪一个号码是别的号码的前缀,有则输出NO,否则输出YES


解法:

Trie 树的基本使用

Trie 结构 的基本表示方法

const int maxnode = 4000 * 100 + 10;
const int sigma_size = 26;

// 字母表为全体小写字母的Trie
struct Trie 
{
  int ch[maxnode][sigma_size];  //存储结点信息,构造成为树。ch[i][j]表示第i个结点字母j的结点的位置
  int val[maxnode];             //对于每一个字符结点的附加信息,比如最后一个结尾字符串做一个标记             
  int sz; // 结点总数
  void clear() 
  { 
		sz = 1; // 初始时只有一个根结点
		memset(ch[0], 0, sizeof(ch[0])); //初始时没有字符
  } 
  int idx(char c) { return c - 'a'; }    // 字符c的编号, ASC码转int

  // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
  void insert(const char *s, int v) 
  {
    int u = 0, n = strlen(s);
    for(int i = 0; i < n; 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; // 字符串的最后一个字符的附加信息为v
  }

  // 找字符串s的长度不超过len的前缀,存入ans容器当中
  void find_prefixes(const char *s, int len, vector<int>& ans) 
  {
    int u = 0;
    for(int i = 0; i < len; i++) 
	{
      if(s[i] == '\0') break;
      int c = idx(s[i]);
      if(!ch[u][c]) break;
      u = ch[u][c];
      if(val[u] != 0) ans.push_back(val[u]); // 找到一个前缀
    }
  }
};

Trie结构一维数组,孩子兄弟链表表示方法

const int maxnode = 4000 * 1000 + 10;
const int sigma_size = 26;

// 字母表为全体小写字母的Trie
struct Trie 
{
  int head[maxnode]; // head[i]为第i个结点的左儿子编号
  int next[maxnode]; // next[i]为第i个结点的右兄弟编号
  char ch[maxnode];  // ch[i]为第i个结点上的字符
  int tot[maxnode];  // tot[i]为第i个结点为根的子树包含的叶结点总数
  int sz; 			 // 结点总数
  void clear()       // 初始时只有一个根结点
  { 
	sz = 1; 
	tot[0] = head[0] = next[0] = 0; 
  } 

  // 插入字符串s(包括最后的'\0'),沿途更新tot
  void insert(const char *s) 
  {
    int u = 0, v, n = strlen(s);
    tot[0]++;
    for(int i = 0; i <= n; i++) 
	{
      // 找字符a[i]
      bool found = false;
      for(v = head[u]; v != 0; v = next[v])
	  {
        if(ch[v] == s[i]) 
		{ // 找到了
          found = true;
          break;
        }
	  }
      if(!found) 
	  {
        v = sz++; // 新建结点
        tot[v] = 0;
        ch[v] = s[i];
        next[v] = head[u];
        head[u] = v; // 插入到链表的首部
        head[v] = 0;
      }
      u = v;
      tot[u]++;
    }
  }
};


poj 3630解法

#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;

///宏定义
const int  INF = 990000000;
const int MAXN = 100000;
const int maxn = MAXN;
///全局变量 和 函数
int T;
int n;

//

const int maxnode = 100000 * 10 + 10;
const int sigma_size = 26;

// 字母表为全体小写字母的Trie
struct Trie 
{
	int ch[maxnode][sigma_size];
	int val[maxnode];
	int sz; // 结点总数
	void clear() 
	{ 
		sz = 1; 
		memset(ch[0], 0, sizeof(ch[0])); 
	} // 初始时只有一个根结点
	int idx(char c) { return c - '0'; } // 求字符编号

	// 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
	void insert(const char *s, int v) 
	{
		int u = 0, n = strlen(s);
		for(int i = 0; i < n; 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; // 字符串的最后一个字符的附加信息为v
	}

	// 找字符串s的长度不超过len的前缀
	void find_prefixes(const char *s, int len, vector<int>& ans) 
	{
		int u = 0;
		for(int i = 0; i < len; i++) 
		{
			if(s[i] == '\0') break;
			int c = idx(s[i]);
			if(!ch[u][c]) break;
			u = ch[u][c];
			if(val[u] != 0) ans.push_back(val[u]); // 找到一个前缀
		}
	}
};

//
char pnumbers[maxn][15];
Trie trie;
int main()
{
	///变量定义
	int i, j, m;
	scanf("%d", &T);
	int cases = 1;
	while(T--)
	{
		trie.clear();
		scanf("%d", &n);
		for (i = 1; i <= n; i++)
		{
			scanf("%s", pnumbers[i]);
			trie.insert(pnumbers[i], i);
		}

		bool flag = true;
		for (i = 1; i <= n; i++)
		{
			vector<int> ans;
			ans.clear();
			trie.find_prefixes(pnumbers[i], strlen(pnumbers[i]), ans);
			if (ans.size() > 1)
			{
				flag = false;
				break;
			}
		}
		if (flag)
		{
			printf("YES\n");
		}
		else
			printf("NO\n");
	}




	///结束
	return 0;
}


poj 3630 采用第二种结构的写法

#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;

///宏定义
const int  INF = 990000000;
const int MAXN = 100000;
const int maxn = MAXN;
///全局变量 和 函数
int T;
int n;

//
const int maxnode = 4000 * 1000 + 10;
const int sigma_size = 26;

// 字母表为全体小写字母的Trie
struct Trie 
{
	int head[maxnode]; // head[i]为第i个结点的左儿子编号
	int next[maxnode]; // next[i]为第i个结点的右兄弟编号
	char ch[maxnode];  // ch[i]为第i个结点上的字符
	int tot[maxnode];  // tot[i]为第i个结点为根的子树包含的叶结点总数
	int sz; 		   // 结点总数
	void clear()       // 初始时只有一个根结点
	{ 
		sz = 1; 
		tot[0] = head[0] = next[0] = 0; 
	} 

	// 插入字符串s(包括最后的'\0'),沿途更新tot
	void insert(const char *s) 
	{
		int u = 0, v, n = strlen(s);
		tot[0]++;
		for(int i = 0; i <= n; i++) 
		{
			// 找字符a[i]
			bool found = false;
			for(v = head[u]; v != 0; v = next[v])
			{
				if(ch[v] == s[i]) 
				{ // 找到了
					found = true;
					break;
				}
			}
			if(!found) 
			{
				v = sz++; // 新建结点
				tot[v] = 0;
				ch[v] = s[i];
				next[v] = head[u];
				head[u] = v; // 插入到链表的首部
				head[v] = 0;
			}
			u = v;
			tot[u]++;
		}
	}

	//
	// 找字符串s的长度不超过len的前缀,存入ans容器当中
	void find_prefixes(const char *s, int len, vector<int>& ans) 
	{
		int u = 0, v;
		for(int i = 0; i < len; i++) 
		{
			if(s[i] == '\0') break;
			for (v = head[u]; v != 0; v = next[v])
			{
				if (ch[v] == s[i])
					break;
			}
			if (v == 0)
				break;
			//寻找当前结点的子节点有没有结束符,如果有,那么找到一个前缀
			for (int cur = head[v]; cur != 0; cur = next[cur])
			{
				if (ch[cur] == '\0')
					ans.push_back(cur); // 找到一个前缀
			}
			u = v;
		}
	}
};

//
char pnumbers[maxn][15];
Trie trie;
int main()
{
	///变量定义
	int i, j, m;
	scanf("%d", &T);
	int cases = 1;
	while(T--)
	{
		trie.clear();
		scanf("%d", &n);
		for (i = 1; i <= n; i++)
		{
			scanf("%s", pnumbers[i]);
			trie.insert(pnumbers[i]);
		}

		bool flag = true;
		for (i = 1; i <= n; i++)
		{
			vector<int> ans;
			ans.clear();
			trie.find_prefixes(pnumbers[i], strlen(pnumbers[i]), ans);
			if (ans.size() > 1)
			{
				flag = false;
				break;
			}
		}
		if (flag)
		{
			printf("YES\n");
		}
		else
			printf("NO\n");
	}
	///结束
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值