問題背景
有一種簡單的網頁判重的方法,通過求兩個網頁內容的最長公共子序列(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;
}