2008百度之星资格赛第二题——网页判重

2008百度之星资格赛第二题——网页判重
问题背景

有一种简单的网页判重的方法,通过求两个网页内容的最长公共子序列(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   : [email protected]
// ============================================================================

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



你可能感兴趣的:(2008百度之星资格赛第二题——网页判重)