E81086713E446D36F62B2AA2A3502B5EB155

          Java雜家

          雜七雜八。。。一家之言

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            40 Posts :: 1 Stories :: 174 Comments :: 0 Trackbacks
          問題背景

          有一種簡單的網頁判重的方法,通過求兩個網頁內容的最長公共子序列(LCS)長度來判定兩個網頁的相似程度。如:
          (網頁A)老師:請用“果然”造句。
          (網頁B)學生:先吃水果,然后喝汽水……
          它們的最長公共子序列為“果然”,長度為2。注意這里的“子序列”并不要求連續。

          類似的,下面兩個網頁:
          (網頁A)老師:請用“果然”造句。
          (網頁B)學生:先吃水果,然后喝汽水,果然拉肚子……

          最長公共子序列還是“果然”,長度為2。但不難看出,由于“果然”兩個字在網頁B中也曾連續出現,第二組網頁比第一組更加“相似”。為了區分開這兩種情況的區分度,我們改用一種稱為LZW的理論。為了嚴格的敘述相似度的計算方法,我們首先定義“文本單元”。

          假定網頁用一個不包含空白字符(空格、回車換行、水平制表符)的字符串來表示。它只包含純文本,沒有標簽。在計算相似度之前,你應該首先對該字符串進行處理,劃分成一個個“文本單元”。每個文本單位可以是一個中文字、英文單詞(由一個或多個連續的半角英文字母和數字組成,正規表達式為[a-zA-Z0- 9]+)、或者一個標點符號。

          根據上述定義,同一個標點符號的全角和半角應該被作為不同的文本單元,盡管他們看起來可能很相近;每個單獨全角英文和全角數字都應該被看成一個單獨的文本單元,而連續的半角英文字母和數字應被看成一個整體。總之,全角的字符可以與中文字同等對待。

          這樣,網頁被看成文本單元序列。例如,網頁“內容?123456??web2.00#”切分出的文本單元序列為(為了顯示方便,用下劃線分隔各文本單元):
          內_容_?_1_2_345_6_?_?_web2_._00_#

          而網頁“why內容相似??1234567890,web#00”的切分結果為:
          why_內_容_相_似_?_?_1234567890_,_web_#_00

          黑體部分給出了兩個網頁的一個公共子序列。注意“內容”、“??”分別在兩個網頁中都是連續出現的文本單元。為了獎勵這種情況,LZW規定一段由連續k個文本單元組成的字符串權值為k^2。在剛才的例子中,“內容”、“??”的權值均為4。但“00”是一個數字串,應當被看成一個單獨的文本單元。所以權值僅為1。

          根據上述規則,公共子序列“內容 ?? 00”的權值為2^2+2^2+1=9。在所有可能的子序列中,這個權值是最大的。

          給定兩個網頁,求他們的LZW相似度,即所有可能的公共子序列中的最大權值。

          注意

          1) 輸入的網頁內容以GBK編碼(參見FAQ)
          2) 除了大小寫英文字母和數字之外的其他半角字符均視為標點符號。
          輸入格式

          包含兩行,分別是網頁A和B對應的字符串(不包含空白字符)。每行至少包含5個字節,最多包含200個字節。
          輸出格式

          輸出僅一行,包含一個整數,為兩個網頁的LZW相似度。
          樣例輸入

          內容?123456??web2.00#
          why內容相似??1234567890,web#00
          樣例輸出
          9


          解答:
          該題主要分兩部分,一部分就是解析成文本單元,另一部分就是LZW的實現,LZW其實是最長公共子序列算法的一個變形。

          //============================================================================
          // Name        : LZW.cpp
          // Author      : Yovn
          // Version     :
          // Copyright   : yovnchine@gmail.com
          //============================================================================

          #include 
          <iostream>
          #include 
          <string>
          #include 
          <cstring>
          using namespace std;

          void parseToLZWLine(const char* in, int inLen, char**& out, int& actualLen) {
              
          int mark=0;
              actualLen
          =0;
              out
          =new char*[inLen];

              
          for (int i=0; i<inLen; i++) {
                  
          char ch=in[i];
                  
          if (ch<0) {
                      
          if (mark<i) {
                          out[actualLen]
          =new char[i-mark+1];
                          strncpy(out[actualLen], in
          +mark, i-mark);
                          out[actualLen][i
          -mark]='\0';
                          actualLen
          ++;
                      }
                      out[actualLen]
          =new char[3];
                      out[actualLen][
          0]=ch;
                      out[actualLen][
          1]=in[++i];
                      out[actualLen][
          2]='\0';
                      actualLen
          ++;

                      mark
          =i+1;
                  } 
          else if ((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9')) {
                      
          continue;
                  } 
          else//only one case left
                  {
                      
          if (mark<i) {
                          out[actualLen]
          =new char[i-mark+1];
                          strncpy(out[actualLen], in
          +mark, i-mark);
                          out[actualLen][i
          -mark]='\0';
                          actualLen
          ++;

                      }
                      out[actualLen]
          =new char[2];
                      out[actualLen][
          0]=ch;
                      out[actualLen][
          1]='\0';
                      actualLen
          ++;
                      mark
          =i+1;
                  }
              }
              
          if (mark<inLen) {
                  out[actualLen]
          =new char[inLen-mark+1];
                  strncpy(out[actualLen], in
          +mark, inLen-mark);
                  out[actualLen][inLen
          -mark]='\0';
                  actualLen
          ++;

              }
          }
          void printLZWStr(char** lzwStr, int lzwLen) {
              
          for (int i=0; i<lzwLen; i++) {
                  printf(
          "%s", lzwStr[i]);
                  
          if (i<lzwLen-1) {
                      printf(
          "_");
                  }
              }
              printf(
          "\n");
          }
          void freeLZWStr(char** lzwStr, int lzwLen) {
              
          for (int i=0; i<lzwLen; i++) {
                  delete[] lzwStr[i];
              }
              delete[] lzwStr;
          }

          int calcLZW(char** left, int leftLen, char** right, int rightLen) {
              
          //printLZWStr(left, leftLen);
              
          //printLZWStr(right, rightLen);
              int** result=new int*[leftLen+1];
              
          int** len=new int*[leftLen+1];
              
          for (int i=0; i<=leftLen; i++) {
                  result[i]
          =new int[rightLen+1];
                  len[i]
          =new int[rightLen+1];
                  result[i][
          0]=0;
                  len[i][
          0]=0;
              }
              memset(result[
          0], 0, sizeof(int)*(rightLen+1));
              memset(len[
          0], 0, sizeof(int)*(rightLen+1));
              
          for (int i=1; i<=leftLen; i++) {
                  
          for (int j=1; j<=rightLen; j++) {
                      
          if (strcmp(left[i-1], right[j-1])==0) {
                          
          //back trace one

                          len[i][j]
          =len[i-1][j-1]+1;
                          result[i][j]
          =result[i-1][j-1]-(len[i-1][j-1]*len[i-1][j-1])
                                  
          +(len[i][j]*len[i][j]);

                      } 
          else if (result[i-1][j]>=result[i][j-1]) {
                          result[i][j]
          =result[i-1][j];
                      } 
          else {
                          result[i][j]
          =result[i][j-1];
                      }

                  }
              }

              
          int ret= result[leftLen][rightLen];
              
          for (int i=0; i<=leftLen; i++) {
                  delete[] result[i];
                  delete[] len[i];

              }
              delete[] result;
              delete[] len;
              
          return ret;

          }

          int main() {
              
          char** lzwStr1=NULL;
              
          char** lzwStr2=NULL;
              string str1;
              string str2;
              
          int lzwLen1=0;
              
          int lzwLen2=0;
              getline(cin,str1);
              getline(cin,str2);
              
              parseToLZWLine(str1.c_str(), strlen(str1.c_str()), lzwStr1, lzwLen1);
              parseToLZWLine(str2.c_str(), strlen(str2.c_str()), lzwStr2, lzwLen2);

              cout
          <<calcLZW(lzwStr1, lzwLen1, lzwStr2, lzwLen2)<<endl;

              freeLZWStr(lzwStr1, lzwLen1);
              freeLZWStr(lzwStr2, lzwLen2);

              
          return 0;
          }



          posted on 2008-05-31 23:20 DoubleH 閱讀(2429) 評論(3)  編輯  收藏

          Feedback

          # re: 2008百度之星資格賽第二題——網頁判重 2008-06-01 09:23 如坐春風
          有意思、  回復  更多評論
            

          # re: 2008百度之星資格賽第二題——網頁判重 2008-06-02 13:59 BT下載
          那我http://www.5a520.cn 小說520 怎算法來定呢?  回復  更多評論
            

          # re: 2008百度之星資格賽第二題——網頁判重 2008-06-03 07:11 lang
          前不久和人討論類似的問題。
          也想到了第二種方法。
          用途是用來給網絡抓取得地理數據排重!
            回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 广汉市| 沅陵县| 班戈县| 建德市| 昭平县| 朔州市| 永川市| 五大连池市| 临泉县| 海原县| 尚志市| 云龙县| 云霄县| 江油市| 隆子县| 洪洞县| 绥中县| 利辛县| 上高县| 辉南县| 若羌县| 三河市| 新平| 朔州市| 陕西省| 长泰县| 丰台区| 富蕴县| 无棣县| 漳州市| 黄大仙区| 梁河县| 武宁县| 崇信县| 高要市| 凌海市| 江口县| 湘西| 沂南县| 屏山县| 太原市|