/**//* 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
搜索
最新评论
|
|