1. 需求
例如用戶給定一個包含與或關(guān)系的關(guān)鍵詞匹配規(guī)則:
(G20&中國&(互聯(lián)網(wǎng)|人工智能|AI)&(騰訊|阿里|阿里巴巴|百度|京東))
需要基于這個規(guī)則采集相關(guān)信息,此時就需要將該規(guī)則解析,看包含哪些詞,再根據(jù)這些詞去采集,需要解析成如下形式才能方便采集:
G20 中國 互聯(lián)網(wǎng) 騰訊
G20 中國 人工智能 騰訊
G20 中國 AI 騰訊
G20 中國 互聯(lián)網(wǎng) 阿里
G20 中國 人工智能 阿里
G20 中國 AI 阿里
G20 中國 互聯(lián)網(wǎng) 阿里巴巴
G20 中國 人工智能 阿里巴巴
G20 中國 AI 阿里巴巴
G20 中國 互聯(lián)網(wǎng) 百度
G20 中國 人工智能 百度
G20 中國 AI 百度
G20 中國 互聯(lián)網(wǎng) 京東
G20 中國 人工智能 京東
G20 中國 AI 京東
行與行之間是或的關(guān)系,行中每個詞之間是并的關(guān)系。
2. 解決方案
考慮到這種規(guī)則表達式類似于算術(shù)表達式,因此我們可以基于算術(shù)表達式對該規(guī)則進行解析:
與算術(shù)表達式類似,我們可以采用兩個棧:
棧1: 存儲規(guī)則符號如(&,|,())
棧2:存儲中間的詞,如中國,互聯(lián)網(wǎng)
這里不用像算術(shù)表達式計算那樣要考慮各種運算符的優(yōu)先級,只需要每次遇到右括號時,將棧1的規(guī)則符號彈出,直到彈出左括號,同時棧2需要彈出左右括號之間相應(yīng)的詞,再根據(jù)彈出的符號是與還是或的關(guān)系,拼裝彈出的詞,為新的詞序列,寫入棧2中。
直到規(guī)則表達式遍歷完且棧1為空,棧2長度為1時,返回棧2元素。
這里需要注意的是,遍歷規(guī)則表達式是以字符為級別,棧2中壓入的元素是以詞為序列的,詞與詞分割的標(biāo)準(zhǔn)即規(guī)則符號(&,|,())
偽代碼:
最終結(jié)果格式為:或之間的詞用#隔開,與之間的詞用空格隔開,如上面例子中的結(jié)果應(yīng)該為:
G20 中國 互聯(lián)網(wǎng) 騰訊#G20 中國 人工智能 騰訊#G20 中國 AI 騰訊#G20 中國 互聯(lián)網(wǎng) 阿里#G20 中國 人工智能 阿里#G20 中國 AI 阿里#G20 中國 互聯(lián)網(wǎng) 阿里巴巴#G20 中國 人工智能 阿里巴巴#G20 中國 AI 阿里巴巴#G20 中國 互聯(lián)網(wǎng) 百度#G20 中國 人工智能 百度#G20 中國 AI 百度#G20 中國 互聯(lián)網(wǎng) 京東#G20 中國 人工智能 京東#G20 中國 AI 京東
stack1 = new Stack() //存儲規(guī)則符號
stack2 = new Stack() //存儲詞
word= ""http:// 記錄一個詞
for(char c <- inputString) ://遍歷輸入序列
if(c 是規(guī)則符號&|()):
if(words長度大于0):
stack2.push(word); //將詞壓入棧2
if(c 是 [&|(]中的任一個):
stack1.push(c)//壓入棧1
if(c是右括號):
list[] list;//記錄此刻彈出的所有詞
while(stack1.size > 0 && stack1.pop != '(') ://彈出規(guī)則符號直到左括號
stack1.pop();
list.add(stack2.pop());
list.add(stack2.pop)
computeres = computKwsByAndOr(list,'&');
stack2.push(computeres);//拼裝結(jié)果壓入棧2;
else(c不是規(guī)則符號,即某個詞的字)://此時需要等到下一個規(guī)則符號時才能組裝成一個詞
word += c //將該字符拼接到上一個字的后面
if(stack1為空 并且stack2只剩一個元素): return reverseString(stack2.pop());
if(stack1有多個元素,且都是一樣的規(guī)則符號(&|),并且stack2不為空):
c1 = stack1.pop();
list = stack2.pop();//彈出stack2的全部元素組成列表;
res = computKwsByAndOr(list,c1);
return reverseString(res);
def computKwsByAndOr(list[] , char c)://拼裝彈出的詞為結(jié)果格式
res = ""
temp = ""
if(c是&):
for(kw <- list):
l = kw.split('#')//找出詞中或的關(guān)系,這樣要一一的和下一個詞與,
//例如list中其中兩個元素是:G20 中國 人工智能#G20 中國 AI
//另一個元素是騰訊,
//這兩個詞序列之間是與的關(guān)系,
//要合并成G20 中國 人工智能 騰訊#G20 中國 AI 騰訊
for(kw_s <- l):
b = res.split('#')
for(kw_s_inner <- b):
temp += kw_s+" " + kw_s_inner + '#'
res = temp;
else if (c是|):
res = String.join(list,'#')
return res;
//由于基于棧是后進先出的,因此最終順序會與人們自己算出的順序不符
//例如(G20&中國&(互聯(lián)網(wǎng)|人工智能))
//上面得到的結(jié)果是人工智能 中國 G20#互聯(lián)網(wǎng) 中國 G20
//為了方便用戶校驗解析結(jié)果,可以將#隔開的或關(guān)系詞組逆序,并且并之間的關(guān)系也逆序:
//即G20 中國 互聯(lián)網(wǎng)#G20 中國 人工智能
def reverseString(String data):
string[] str = data.split('#');//先用#分割
list;
for(s <- str) :
string[] inner_str = s.split(' ');//再用空格分割
if(inner_str.length > 1) :
string str_append;
for(i:inner_str.length-1->0): //逆序拼接
str_append += inner_str[i]+' '
list.add(str_append);
list.reverse();
return list.join('#');
3.具體代碼
具體代碼實現(xiàn)參照GitHub
https://github.com/qmh2014/keywordsExtract