E81086713E446D36F62B2AA2A3502B5EB155

Java杂家

杂七杂八。。。一家之言

BlogJava 首页 新随笔 联系 聚合 管理
  141 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 阅读(2426) 评论(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
前不久和人讨论类似的问题。
也想到了第二种方法。
用途是用来给网络抓取得地理数据排重!
  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: