 /**//*
N<=10000個文本串,M<=10000個要查詢的串
對每個要查詢的串,有多少個文本串與該串的編輯距離<=1
若編輯距離為0,輸出-1

建立Trie樹,對于要查詢的串,狀態(tài)(root, p, change)
表示當前在節(jié)點root,查詢的串檢查到p,change表示有沒修改過
------root及p之前的都匹配了,當前就是root的兒子跟p檢查了

有匹配,添加,刪除,替換
注意的是,查詢的是文本串的個數(shù),因為可能不同的變換,會導(dǎo)致到達同一個狀態(tài)
-------需要判重,一方面加快速度,另一方面不會重復(fù)計數(shù)
大數(shù)組判重,每次清空的話會很花時間,用cases數(shù)標記一下,表示在當前這個case
已經(jīng)訪問過

注意到達最后時,*p == 0 時,這個位置還可以再插入的,不要立即返回了

hit 2888是升級版
是求前綴,要注意重復(fù)計數(shù),還有清空問題等
*/
#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,經(jīng)過一些變換,
if (*p == 0) 說明匹配了,這個根到這個節(jié)點就是一個合法的前綴了
所以狀態(tài) (root, p, edth) 當前樹的節(jié)點root,串匹配到p(p之前的已經(jīng)和root及之前的匹配了),可用距離edth
如果某個節(jié)點已經(jīng)mark為2,表明它被某種變換匹配了,就不用繼續(xù)搜了
if (root->mark == 2) {
return;
}
這里又不是求變換的方法數(shù),所以之前的變換已經(jīng)可行了,當前的這種變換不用搞下去了,直接return
然后對走過的路徑mark為1,為了以后找到這些走過的節(jié)點,計算總數(shù),還有清空,避免走無用的位置
注意某個節(jié)點mark為2,有可能它下面的某個節(jié)點也被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,也有可能下面的節(jié)點也有2,要繼續(xù)清空
 if (root->ch[i] != NULL) {
ans += gao(root->ch[i]);
}
}
 if (root->mark == 2) {//只保留離根最近的那個值
ans = root->cnt;
}
root->mark = 0;
return ans;
}
|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|