乐师理工acm集训-字典树

文章目录

  • HihoCoder1014 Trie树【字典树】
    • 解题思路
    • AC代码
  • POJ2001 Shortest Prefixes【字典树】
    • 题目大意
    • 解题思路
    • AC代码
  • HDU2072 单词数【字典树/set + 输入处理】
    • 说明
    • 解题思路
    • AC代码1【字典树 + 普通处理】
    • AC代码2【set + stringstream流】
    • AC代码3【set + strtok】
  • HDU1247 Hat’s Words【字典树/set + 单词拆分】
    • 题目大意
    • 解题思路
    • AC代码1【字典树 + 拆分】
    • AC代码2【set + 拆分】

HihoCoder1014 Trie树【字典树】

传送门:HihoCoder1014 Trie树

解题思路

  建立字典树,过程中记录每个字母结点走过的次数。
  注意:结点数组要开 单词个数*单词长度 大小。因为最坏的情况是每次插入的单词都和已有的单词没有公共前缀。

AC代码

#include
using namespace std;
const int MAXN = 1e6+5;
int tree[MAXN][26];
int vis[MAXN],node = 1;
char s[MAXN];
void insert(char s[])
{
	int len = strlen(s);
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch]) // 没有结点
		{
			tree[p][ch] = node++; // 分配一个结点
		}
		p = tree[p][ch];
		vis[p]++; // 统计结点访问次数
	}
	return ;
}
int search(char s[])
{
	int len = strlen(s);
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if(!tree[p][ch]) // 没有对应结点
		{
			return 0;
		}
		p = tree[p][ch];
	}
	return vis[p]; 
}
int main()
{
	int n;
	scanf("%d",&n);
	while (n--)
	{
		scanf("%s",&s);
		insert(s);
	}
	scanf("%d",&n);
	while (n--)
	{
		scanf("%s",&s);
		printf("%d\n",search(s));
	}
}

POJ2001 Shortest Prefixes【字典树】

传送门:POJ2001 Shortest Prefixes

题目大意

  给定一组单词,找出对于每个单词能唯一(无歧义)标识该单词的最短前缀。

解题思路

  根据给定的一组单词建立字典树,对于每个单词找到第一个不是公共前缀的字母即可。

AC代码

#include
#include
using namespace std;
const int MAXN = 2e4+5;
int tree[MAXN][26];
int vis[MAXN],node = 1;
char s[MAXN][21];
void insert(char s[])
{
	int len = strlen(s);
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch])
		{
			tree[p][ch] = node++;
		}
		p = tree[p][ch];
		vis[p]++;
	}
	return ;
}
void search(char s[])
{
	int len = strlen(s);
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		p = tree[p][ch];
		putchar(s[i]); // 输出字母,直到不在公共前缀里
		// 只访问了一次,说明该字母不在公共前缀里
		// 则 公共前缀 + 该字母 能唯一标识该单词
		if (vis[p] == 1)
		{
			return ;
		}
	}
}
int main()
{
	int ind = 0;
	while (~scanf("%s",&s[ind]))
	{
		insert(s[ind]);
		ind++;
	}
	for (int i = 0; i < ind; i++)
	{
		printf("%s ",s[i]);
		search(s[i]);
		printf("\n");
	}
}

HDU2072 单词数【字典树/set + 输入处理】

传送门:HDU2072 单词数

说明

  这道题主要麻烦在输入是一行,要对其进行单词的提取,而且可能会存在单词间间隔多个空格或末尾是空格的情况。

解题思路

  对于一行中的每个单词,判断是否出现过,如果没出现过,将其加入已出现集合并把个数加一,如果出现过则不作处理。
  对于判断是否出现过和加入已出现集合可以采用字典树或者set。

AC代码1【字典树 + 普通处理】

#include
using namespace std;
const int MAXN =  5e4 + 5;
int tree[MAXN][30];
int vis[MAXN*100],node = 1;
void insert(string s)
{
	int len = s.size();
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch])
		{
			tree[p][ch] = node++;
		}
		p = tree[p][ch];
	}
	vis[p] = 1; // 标记结尾	
}
bool search(string s)
{
	int len = s.size();
	int p = 0,cnt = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch])
		{
			return 0;
		}
		p = tree[p][ch];
	}
	return vis[p];
}
int main()
{
	string s,temp;
	while (getline(cin,s) && s[0] != '#')
	{
		int len = s.size(), ans = 0;
		temp = "";
		// 提取出一行中的每个单词
		for (int i = 0; i < len; i++)
		{
			if(s[i] == ' ')
			{
				// 单词不为空且字典树中没有
				if (temp != "" && !search(temp))
				{
					ans++;
					insert(temp);
					temp = ""; // 重置
				}
			}
			else
			{
				temp += s[i]; // 复制单词
			}
		}
		// 别忘了处理最后一个
		if (temp != "" && !search(temp))
		{
			ans++;
		}
		cout << ans << "\n";
		memset(tree,0,sizeof(tree));
		memset(vis,0,sizeof(vis));
		node = 1;
	}
	return 0;
}

AC代码2【set + stringstream流】

#include
using namespace std;
const int MAXN =  5e4 + 5;
set<string> st;
int main()
{
	string s,temp;
	while (getline(cin,s) && s[0] != '#')
	{
		stringstream str(s);
		while (str >> temp)
		{
			st.insert(temp);
		}
		cout << st.size() << "\n";
		st.clear();
	}
	return 0;
}

AC代码3【set + strtok】

#include
using namespace std;
const int MAXN =  5e4 + 5;
set<string> st;
char s[MAXN], *p;
int main()
{
	while (gets(s) && s[0] != '#')
	{
		p = strtok(s, " ");
		while (p)
		{
			st.insert(p);
			p = strtok(NULL," ");
		}
		cout << st.size() << "\n";
		st.clear();
	}
	return 0;
}

HDU1247 Hat’s Words【字典树/set + 单词拆分】

传送门:HDU1247 Hat’s Words

题目大意

  给定一组单词,若某个单词能由其他两个单词拼接而成,就输出它。
  注意:同一个单词用两次,以此拼接成另一个单词也是可以的。

输入
a
aa
输出:
aa
解释:
a + a = aa

解题思路

  先将所有单词存储起来,然后对每个单词进行拆分,判断被拆分的两部分是否是单独的单词。

AC代码1【字典树 + 拆分】

#include
using namespace std;
const int MAXN =  5e4 + 5;
int tree[MAXN][30];
int vis[MAXN*100],node = 1;
char s[MAXN][100],s1[100],s2[100];;
void insert(char s[])
{
	int len = strlen(s);
	int p = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch])
		{
			tree[p][ch] = node++;
		}
		p = tree[p][ch];
	}
	vis[p] = 1; // 标记单词结尾	
}
bool search(char s[])
{
	int len = strlen(s);
	int p = 0,cnt = 0;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		if (!tree[p][ch])
		{
			return 0;
		}
		p = tree[p][ch];
	}
	return vis[p];
}
int main()
{
	int ind = 0;
	while (~scanf("%s",&s[ind]))
	{
		insert(s[ind]);
		ind++;
	}
	for (int i = 0; i < ind; i++)
	{
		int len = strlen(s[i]);
		for (int j = 1; j < len; j++)
		{
			// 复制全部
			strcpy(s1,s[i]);
			// 再从 j 位置截断
			s1[j] = '\0'; 
			// 复制后一部分
			strcpy(s2,s[i] + j);
			// 如果两部分都是单独的单词
			if (search(s1) && search(s2))
			{
				printf("%s\n",s[i]);
				// 该单词判断结束
				break;
			}
		}
	}
	return 0;
}

AC代码2【set + 拆分】

#include
using namespace std;
const int MAXN =  5e4 + 5;
set<string> st;
int main()
{
	string s, a, b;
	while (cin >> s)
	{
		st.insert(s);
	}
	for (auto it : st)
	{
		int size = it.size();
		for (int i = 1; i < size; i++)
		{
			a = it.substr(0, i);
			b = it.substr(i, size - i);
			if (st.count(a) && st.count(b))
			{
				cout << it << "\n";
				break;
			}
		}
	}
	return 0;
}

你可能感兴趣的:(2020暑假集训)