2. LZW網頁判重 (20分)

2. LZW網頁判重 (20分)

問題背景

有一種簡單的網頁判重的方法,通過求兩個網頁內容的最長公共子序列(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個文本單元組成的字符串權值爲k2。在剛纔的例子中,“內容”、“??”的權值均爲4。但“00”是一個數字串,應當被看成一個單獨的文本單元。所以權值僅爲1。

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

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

注意

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

輸入格式

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

輸出格式

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

樣例輸入

內容?123456??web2.00#
why內容相似??1234567890,web#00

樣例輸出

9

樣例解釋

儘管兩個網頁裏看上去都有“123456”但一方面第一個網頁中混雜的全角和半角字符,而另一方面,即使全部改成半角字符,由於數字串“123456”和“1234567890”將分別看成一個單獨的文本單元,因此無法部分匹配。

 

 

全角和半角不知道怎麼編程區分,想了很長時間。以前編的程序,沒遇到這個問題,哎。

我不懂怎麼編,是別人的代碼:

 

#include "iostream"
using namespace std;

struct mystr
{
    char *s;
    struct mystr * next;    
    mystr(){s=NULL; next=NULL;}
};
bool getstr(char *s, int n);
void split(char *,mystr &);
int getweight(mystr &substr1,mystr &substr2);
void strcpy(char *s,char *d, int start,int len)
{
     int i;
     for(i=0;i<len;i++ ) d[i]=s[start+i];
     d[i]=0;
}
int main()
{
    char s1[201],s2[201];
    getstr(s1,200);
    getstr(s2,200);
    mystr substr1,substr2;
    split(s1,substr1);
    split(s2,substr2);
    int weight= getweight(substr1,substr2);
    cout<<weight;
   return 0;
}

bool getstr(char *s, int n)
{
    char ch;
    int i;
    for(i=0; (ch=getchar())!='/n'&&i<n;i++) s[i]=ch;
    s[i]=0;
    return true;  
}
void split(char *s,mystr &substr)//字符分爲三類,全角(符號和漢字),半角數字和字母,半角標點
{
    mystr *p=&substr;
    int i,j;
    i=0,j=0;
    while(s[i])
      {
        while(s[i]==' ') i++;//過濾空格
        if (!s[i]) break;
       p->next=new mystr;
        p=p->next;
       if (s[i]&128)//全角字符,包括漢字
        {
            p->s=new char[3];
            strcpy(s,p->s,i,2);
            i+=2;
            j=i;
            // continue;
        }
       else //if(s[i]>='0'&&s[i]<='9'||s[i]>='a'&&s[i]<='z'||s[i]>='A'&&s[i]<='Z')
          {
             j=i;
             while(s[j]>='0'&&s[j]<='9'||s[j]>='a'&&s[j]<='z'||s[j]>='A'&&s[j]<='Z') j++;
             if (j!=i)
               {
                p->s=new char[j-i+1];
                strcpy(s,p->s,i,j-i);
                i=j;     
               }
            else
             {
              p->s=new char[2];
              strcpy(s,p->s,i,1);
              i++;  
             }
         
          }
      }
}


int getweight(mystr &substr1,mystr &substr2)
{
    mystr *p1=substr1.next;
    mystr *p2=substr2.next;
    mystr *pto1,*pto2;
    int weight=0,count=0;
    while(p1)
      {
       while(p2)
       {
        pto1=p1,pto2=p2;
        count=0;
        while(!strcmp(pto1->s,pto2->s))
        {
         count++;
         pto1=pto1->next;
         pto2=pto2->next;
        }
        weight+=count*count;
        if (pto2!=p2)     p2=pto2;    
        else p2=p2->next;
       }
       p1=p1->next;
      }   
    return weight;
}

發佈了43 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章