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