题意: 模拟搜索引擎的模糊搜索功能。给出n个字符串作为数据库,n<=30W。现在给出一个字符串,问你在把这个串在一定次数限制内修改后它是数据库内多少个串的前缀。修改操作包括增加字母,删除字母,改变单个字母。修改操作的次数限制不超过2,所有的串的长度不超过10。
解法:
首先知道一个事实,修改询问串的目的是让修改后的询问串能够与某个串的前缀相同,因此若数据库中不存在某种前缀的字符串,那么倾向这个前缀变化的字符串也是不必要的,因此,将询问串直接插入字典树中匹配,若在某个节点失败,就使用修改操作使匹配继续进行。
先将作为数据库的n个串插入一棵字典树。然后对于一个询问串,将它放进字典树内匹配,用成功完全匹配的节点构造另外一棵匹配树。然后在匹配树上dfs一遍,若在某个节点遇到了单词结尾的标记,则在原树中经过这个节点的所有串的数量计入sum并且返回,因为以这个节点作为根的子树包含的串已经都被计算过了一次,避免重复。
本题有一个基于常识的大坑,当允许的修改数大于询问串的长度时,输出的是0,而不是n,这样卡了一晚上,so蛋疼。。
UVaLive上貌似不能评测这题,要在UVa上交。
/* **********************************************
Author : Nero
Created Time: 2013-8-28 22:42:35
Problem id : UVA 1462
Problem Name: Fuzzy Google Suggest
*********************************************** */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
struct Node {
int c[26];
int cnt;
bool exs;
}node[3510000];
int tot , sum ;
int New_node() {
clr(node[tot].c,-1);
node[tot].cnt = 0;
node[tot].exs = 0;
return tot ++;
}
void init() {
tot = 0;
New_node();
}
void insert(char *s) {
int now = 0;
node[now].cnt ++;
for( ; *s ; s ++) {
int a = (*s) - 'a';
if(node[now].c[a] == -1) node[now].c[a] = New_node();
now = node[now].c[a];
node[now].cnt ++;
}
}
void Super_man(int now, int xnow, char *s, int lim) {
if(node[xnow].cnt == 1) return ;
if((*s) == 0) {
node[xnow].exs = 1;
node[xnow].cnt = 1;
return ;
}
if(lim) Super_man(now, xnow, s+1, lim-1);
int a = (*s) - 'a';
for(int i = 0; i < 26; i ++) if(node[now].c[i] != -1) {
if(node[xnow].c[i] == -1) node[xnow].c[i] = New_node();
if(a == i) {
Super_man(node[now].c[i], node[xnow].c[i], s+1, lim);
}
if(lim) {
Super_man(node[now].c[i], node[xnow].c[i], s+1, lim-1);
Super_man(node[now].c[i], node[xnow].c[i], s, lim-1);
}
if(node[xnow].c[i] == -1) continue;
if(node[node[xnow].c[i]].exs == 0) tot --, node[xnow].c[i] = -1; else node[xnow].exs = 1;
}
}
void Count(int now, int xnow) {
if(node[xnow].cnt == 1) {
sum += node[now].cnt;
return ;
}
for(int i = 0; i < 26; i ++) if(node[xnow].c[i] != -1) {
Count(node[now].c[i], node[xnow].c[i]);
}
}
void Solve(char *s, int lim, int Irt) {
int len = strlen(s);
if(len <= lim) return ;
Super_man(0, Irt, s, lim);
Count(0,Irt);
}
int main() {
int n, nq, d, Irt;
char s[11];
init();
scanf("%d", &n);
while(n--) {
scanf("%s", s);
insert(s);
}
scanf("%d", &nq);
int haha = tot;
for(int i = 1; i <= nq; i ++) {
scanf("%s%d", s, &d);
Irt = New_node();
sum = 0;
Solve(s,d, Irt);
printf("%d\n", sum);
tot = haha;
}
return 0;
}
尝试修改成下面的一棵树的写法,居然跑得更慢了- -
/* **********************************************
Author : Nero
Created Time: 2013-8-28 22:42:35
Problem id : UVA 1462
Problem Name: Fuzzy Google Suggest
*********************************************** */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
struct Node {
int c[26];
int cnt;
int lev;
int mark;
}node[3001000];
int tot , sum , Lev;
int New_node() {
clr(node[tot].c,-1);
node[tot].cnt = 0;
node[tot].lev = 0;
node[tot].mark = 0;
return tot ++;
}
void init() {
tot = 0;
New_node();
}
void insert(char *s) {
int now = 0;
node[now].cnt ++;
for( ; *s ; s ++) {
int a = (*s) - 'a';
if(node[now].c[a] == -1) node[now].c[a] = New_node();
now = node[now].c[a];
node[now].cnt ++;
}
}
void Super_man(int now, char *s, int lim) {
if(node[now].mark == Lev) return ;
if((*s) == 0) {
node[now].lev = Lev;
node[now].mark = Lev;
return ;
}
if(lim) Super_man(now, s+1, lim-1);
int a = (*s) - 'a';
if(lim < 0) while(1);
for(int i = 0; i < 26; i ++) if(node[now].c[i] != -1) {
if(a == i) {
Super_man(node[now].c[i], s+1, lim);
}
else if(lim) {
Super_man(node[now].c[i], s+1, lim-1);
Super_man(node[now].c[i], s, lim-1);
}
if(node[node[now].c[i]].lev == Lev) node[now].lev = Lev;
}
}
void Count(int now) {
if(node[now].mark == Lev) {
sum += node[now].cnt;
return ;
}
for(int i = 0; i < 26; i ++) if(node[now].c[i] != -1 && node[node[now].c[i]].lev == Lev) {
Count(node[now].c[i]);
}
}
void Solve(char *s, int lim) {
int len = strlen(s);
if(len <= lim) return ;
Super_man(0, s, lim);
Count(0);
}
int main() {
int n, nq, d;
char s[21];
scanf("%d", &n);
init();
while(n--) {
scanf("%s", s);
insert(s);
}
scanf("%d", &nq);
for(int i = 1; i <= nq; i ++) {
scanf("%s%d", s, &d);
sum = 0;
Lev = i;
Solve(s,d);
printf("%d\n", sum);
}
return 0;
}