 /**//*
N<=10000个文本串,M<=10000个要查询的串
对每个要查询的串,有多少个文本串与该串的编辑距离<=1
若编辑距离为0,输出-1

建立Trie树,对于要查询的串,状态(root, p, change)
表示当前在节点root,查询的串检查到p,change表示有没修改过
------root及p之前的都匹配了,当前就是root的儿子跟p检查了

有匹配,添加,删除,替换
注意的是,查询的是文本串的个数,因为可能不同的变换,会导致到达同一个状态
-------需要判重,一方面加快速度,另一方面不会重复计数
大数组判重,每次清空的话会很花时间,用cases数标记一下,表示在当前这个case
已经访问过

注意到达最后时,*p == 0 时,这个位置还可以再插入的,不要立即返回了

hit 2888是升级版
是求前缀,要注意重复计数,还有清空问题等
*/
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<cmath>
#include<string>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<set>
#include<list>
#include<numeric>
#include<cassert>
#include<ctime>

using namespace std;

const int MAXN = 200010;

 struct Trie {
int cnt;
Trie *ch[26];
};

Trie nodes[MAXN], *ptr, *root;

char str[30];
int mark[MAXN][21][2], cases;

 Trie* newNode() {
ptr->cnt = 0;
fill(ptr->ch, ptr->ch+26, (Trie*)NULL);//
return ptr++;
}

 void insert(Trie *root, char *p) {
 if (*p == 0) {
root->cnt ++;
return ;
}
 if (root->ch[*p-'a'] == NULL) {
root->ch[*p-'a'] = newNode();
}
insert(root->ch[*p-'a'] , p+1);
}


 int gao(Trie *root, char *p , int change) {
 if(mark[root-nodes][p-str][change>0] == cases) {
return 0;
}
mark[root-nodes][p-str][change>0] = cases;
 if (*p == 0) {
int tot = root->cnt;
 if( !change ) {
 if( root->cnt > 0 ) {
return -1;
}
 for ( int i = 0; i < 26; i++) { // add at last
 if( root->ch[i] != NULL) {
tot += root->ch[i]->cnt;
mark[root->ch[i]-nodes][p-str][1] = cases;//note!!!!
}
}
}
return tot;
}

int tot = 0;
//match *p
 if (root->ch[*p-'a'] != NULL) {
int tmp = gao(root->ch[*p-'a'] , p+1, change);
 if (tmp == -1) {
return -1;
}
tot += tmp;
}
 if(!change) {
//del
tot += gao(root, p+1, 1);
 for (int i = 0; i < 26; i++) {
 if (root->ch[i] != NULL ) {
//add
tot += gao(root->ch[i], p , 2);
 if( i != *p-'a') {
//replace
tot += gao(root->ch[i], p+1 , 4);
}
}
}
}
return tot;
}

int main()
  {

#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
#endif

 for ( int n, m; ~scanf("%d%d", &n, &m); ) {
ptr = nodes;
root = newNode();
 for (int i = 0; i < n ; i++) {
scanf("%s", str);
insert(root, str);
}
 for (int i = 0 ;i < m; i++) {
cases++;
scanf("%s", str);
printf("%d\n", gao(root, str, 0));
}
}
return 0;
}

以下是hit 2888的核心部分
 /**//*
对于串str,经过一些变换,
if (*p == 0) 说明匹配了,这个根到这个节点就是一个合法的前缀了
所以状态 (root, p, edth) 当前树的节点root,串匹配到p(p之前的已经和root及之前的匹配了),可用距离edth
如果某个节点已经mark为2,表明它被某种变换匹配了,就不用继续搜了
if (root->mark == 2) {
return;
}
这里又不是求变换的方法数,所以之前的变换已经可行了,当前的这种变换不用搞下去了,直接return
然后对走过的路径mark为1,为了以后找到这些走过的节点,计算总数,还有清空,避免走无用的位置
注意某个节点mark为2,有可能它下面的某个节点也被mark为2,要注意只保留离根最近的那个
*/

 void gao(Trie *root, char *p , int edth) {
 if (root->mark == 2) {
return;
}
root->mark = 1;
 if (*p == 0) {
root->mark = 2;
return ;
}
//match *p
 if (root->ch[*p-'a'] != NULL) {
gao(root->ch[*p-'a'] , p+1, edth);
}
 if(edth > 0) {
//del
gao(root, p+1, edth - 1);
 for (int i = 0; i < 26; i++) {
 if (root->ch[i] != NULL) {
//add
gao(root->ch[i], p , edth - 1);
 if( i != *p-'a') {
//replace
gao(root->ch[i], p+1 , edth - 1);
}
}
}
}
return ;
}

 int gao(Trie *root) {//cal and clear
 if (root -> mark == 0) {
return 0;
}
int ans = 0;
 for (int i = 0; i < 26 ; i ++) {//mark为2,也有可能下面的节点也有2,要继续清空
 if (root->ch[i] != NULL) {
ans += gao(root->ch[i]);
}
}
 if (root->mark == 2) {//只保留离根最近的那个值
ans = root->cnt;
}
root->mark = 0;
return ans;
}
|
|
常用链接
随笔分类
Links
搜索
最新评论

|
|