@(K ACMer)
题意:
依次给 n 个全部由小写字母构成且长度不超过 300 的非空字符串求每个字符串之前的字符串中与当前字符串完全相同的个数.
分析:
一种思路:
很直接的可以用map来查,但是map节点之间比较的时候时间比较长,就把字符串hash一下,开始裸的hash wa了,优化了hash函数加了个长度相关也就过了.
这里主要学习trie树实现的思路:
树节点维护的是当前节点为结尾的字符串的个数,而不是通常的当前节点为结尾的前缀的个数.和所有儿子结点的指针.
关于trie树详见代码说明.
code:
字符串hash + map
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = int(1e9) + 7, INF = 0x3fffffff, maxn = 1e7 + 40;
typedef unsigned long long ull;
ull mods = 1e20;
int n, seed = 13131;
ull gethash(string s)
{
ull ret = 0;
for (int i = 0; i < s.size(); i++) {
ret = (ret * seed + s[i] - 'a') % mods;
}
return ret - s.size() * seed;
}
int main(void) {
while (cin >> n) {
map<ull, int> mp;
while (n--) {
string s;
cin >> s;
ull k = gethash(s);
if (mp.count(k))
cout << mp[k] << endl;
else cout << 0 << endl;
mp[k]++;
}
}
return 0;
}
tire树:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = int(1e9) + 7, INF = 0x3fffffff, maxn = 1e7 + 40;
char str[309];
#define MAX 26
typedef struct trienode
{
int num; // 该节点为结尾的字符串出现的次数
struct trienode *next[MAX]; //该节点的后续节点
} trienode;
trienode memory[10000000 / 4]; //先分配好内存。 malloc 较为费时
int mcnt = 0; // 内存计数器
//初始化一个节点。nCount计数为1, next都为null
trienode* create(void)
{
trienode* tmp = &memory[mcnt++]; //节点指向一个已经分配好的内存节点之一.
tmp -> num = 0; //当前节点初始化个数为1
for (int i = 0; i < MAX; i++) //当前节点的所有儿子的指针初始化为null
tmp->next[i] = NULL;
return tmp; //返回分配好的当前节点的指针
}
void insert(trienode* tmp, char* str) //把字符串加入tire树中
{
int i = 0, k;
while (str[i]) { //一个一个的插入字符
k = str[i++] - 'a'; //当前字符 应该插入的位置
if (tmp -> next[k] )
;
else
tmp -> next[k] = create(); //如果该节点的该儿子不存在,就创建一个
tmp = tmp -> next[k]; // 树往高深度进展
if (str[i] == '\0') tmp -> num++;
}
}
int search(trienode* root, char * str) //查找str为前缀的字符串,在树中的个数
{
if (root == NULL) //如果根节点为空,返回0
return 0;
trienode* tmp = root; //当前节点
int i = 0, k;
while (str[i]) { //一直查找完整个字符串放可罢休
k = str[i++] - 'a'; //当前节点对应的儿子的位置
if (tmp -> next[k]) //如果儿子存在,就递归到儿子
tmp = tmp->next[k];
else
return 0; //前缀已经查完了,退出
}
return tmp -> num; //返回最后的那个字符,所在节点的nCount(也就是这个字符串在原串中的数量)
}
int main(void)
{
int n;
while (~scanf("%d", &n)) {
trienode* rt = create();
while (n--) {
scanf("%s", str);
printf("%d\n", search(rt, str));
insert(rt, str);
}
}
return 0;
}