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;
}