题意:
给定一个电话号码本,看看有没有哪一个号码是别的号码的前缀,有则输出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;
}