实现一种简单原始的文件相似度计算,即以两文件的公共词汇占总词汇的比例来定义相似度。为简化问题,这里不考虑中文(因为分词太难了),只考虑长度不小于3、且不超过10的英文单词,长度超过10的只考虑前10个字母。
输入格式:
输入首先给出正整数N(≤100),为文件总数。随后按以下格式给出每个文件的内容:首先给出文件正文,最后在一行中只给出一个字符#,表示文件结束。在N个文件内容结束之后,给出查询总数M(≤10
4
),随后M行,每行给出一对文件编号,其间以空格分隔。这里假设文件按给出的顺序从1到N编号。
输出格式:
针对每一条查询,在一行中输出两文件的相似度,即两文件的公共词汇量占两文件总词汇量的百分比,精确到小数点后1位。注意这里的一个“单词”只包括仅由英文字母组成的、长度不小于3、且不超过10的英文单词,长度超过10的只考虑前10个字母。单词间以任何非英文字母隔开。另外,大小写不同的同一单词被认为是相同的单词,例如“You”和“you”是同一个单词。
输入样例:
3
Aaa Bbb Ccc
Bbb Ccc Ddd
Aaa2 ccc Eee
is at Ddd@Fff
2
1 2
1 3
输出样例:
50.0%
33.3%
第一种做法:直接用数组做,结果,最后一个点超时,求大佬帮看看还能怎么优化!!!
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_TEXT 10001
int mystricmp(char *str1, char *str2);
typedef struct {
char Data[MAX_TEXT][11];
int Article_Size;
int rea_size;
} Article;
Article artice[MAX_TEXT];
char text[20];
int main() {
int n;
scanf("%d", &n);
char ch;
for (int i = 0; i < n; i++) {
int j = 0, size = 0, begin = 0, save = 0;
getchar();
while ((ch = getchar()) != '#') {
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) {
text[j++] = ch;
if (j == 10) {
text[j] = '\0';
strcpy(artice[i].Data[save++], text);
size++;
j = 0;
}
} else {
if (j >= 3) {
text[j] = '\0';
strcpy(artice[i].Data[save++], text);
size++;
}
j = 0;
}
}
artice[i].Article_Size = size;
}
for (int i = 0; i < n; i++) {
int rea_size = artice[i].Article_Size;
for (int j = 0; j < artice[i].Article_Size; j++) {
if (artice[i].Data[j][0] == ' ') {
continue;
}
for (int k = 0; k < artice[i].Article_Size; k++) {
if (j == k || artice[i].Data[k][0] == ' ') {
continue;
}
if (mystricmp(artice[i].Data[j], artice[i].Data[k])) {
artice[i].Data[k][0] = ' ';
rea_size--;
}
}
}
artice[i].rea_size = rea_size;
}
int m, a, b;
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d %d", &a, &b);
a--;
b--;
int equ = 0;
for (int j = 0; j < artice[a].Article_Size; j++) {
int flag = 0;
for (int k = 0; k < artice[b].Article_Size; k++) {
if (artice[b].Data[k][0] == ' ') {
continue;
}
if (mystricmp(artice[a].Data[j], artice[b].Data[k])) {
artice[b].Data[k][0] = ' ';
flag = 1;
break;
}
}
if (flag) {
equ++;
}
}
int total = (artice[a].rea_size - equ) + (artice[b].rea_size - equ)
+ equ;
printf("%.1f%%\n", (double) equ / total * 100);
}
return 0;
}
int mystricmp(char *str1, char *str2) {
int flag = 1, move = 0, times = 0;
char a = *(str1 + move);
char b = *(str2 + move);
while (a != '\0' && b != '\0') {
if (!(a == b || a + ('a' - 'A') == b || a - ('a' - 'A') == b
|| b + ('a' - 'A') == a || b - ('a' - 'A') == a)) {
flag = 0;
break;
}
move++;
times++;
if (times == 10) {
break;
}
a = *(str1 + move);
b = *(str2 + move);
}
return flag;
}
第二种做法:吸取上面的教训,发现后面再去掉相同的单词太花时间(得到去重的文章单词数量),改用链表,一个Insert就可以得到预期结果了,但还是超时最后一个点,求大佬帮看看还能怎么优化!!!
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_VIS 10001
#define MAX_TEXT 101
typedef struct W{
char Data[11];
struct W*Next;
} WordNode;
typedef struct{
WordNode *Words;
int Article_Word_Size;
} Article;
Article artice[MAX_TEXT];
char text[20];
int My_Stricmp(char *str1,char *str2);
void Init_Article();
WordNode* Insert_Word(WordNode*root,char *insertWord,int *size);
int Find_Equeal_Word(WordNode *a,WordNode*b);
int main(){
int n,i,j,size;
scanf("%d",&n);
char ch;
for(i=0; i<n; i++){
j=0;size=0;
getchar();
while((ch=getchar())!='#'){
if(('a'<=ch && ch<='z') || ('A'<=ch && ch<='Z')){
text[j++]=ch;
if(j==10){
text[j]='\0';
artice[i].Words=Insert_Word(artice[i].Words,text,&size);
j=0;
while((ch=getchar())){
if(!('a'<=ch && ch<='z') || ('A'<=ch && ch<='Z')){
break;
}
}
}
}else{
if(j>=3){
text[j]='\0';
artice[i].Words=Insert_Word(artice[i].Words,text,&size);
}
j=0;
}
}
artice[i].Article_Word_Size=size;
}
int m,a,b,equ,total;
scanf("%d",&m);
for(i=0; i<m; i++){
scanf("%d %d",&a,&b);
a--;b--;
equ=Find_Equeal_Word(artice[a].Words,artice[b].Words);
total=(artice[a].Article_Word_Size-equ)+(artice[b].Article_Word_Size-equ)+equ;
printf("%.1f%%\n",(double)equ/total*100);
}
return 0;
}
int My_Stricmp(char *str1,char *str2){
int flag=0,move=0;
char a=*(str1+move);
char b=*(str2+move);
while(a!='\0' && b!='\0'){
if(!(a==b
|| a+('a'-'A')==b || a-('a'-'A')==b
|| b+('a'-'A')==a || b-('a'-'A')==a)){
flag=1;
break;
}
move++;
a=*(str1+move);
b=*(str2+move);
}
return flag;
}
void Init_Article(){
for(int i=0; i<MAX_TEXT; i++){
artice[i].Words=NULL;
}
}
WordNode* Insert_Word(WordNode*root,char *insertWord,int *size){
if(root==NULL){
(*size)++;
WordNode *temp=(WordNode*)malloc(sizeof(WordNode));
strcpy(temp->Data,insertWord);
temp->Next=NULL;
return temp;
}
if(!My_Stricmp(root->Data,insertWord)){
return root;
}
int flag=1;
WordNode *move=(WordNode*)malloc(sizeof(WordNode));
move=root;
while(move->Next!=NULL){
move=move->Next;
if(!My_Stricmp(move->Data,insertWord)){
flag=0;
break;
}
}
if(flag){
(*size)++;
WordNode *temp=(WordNode*)malloc(sizeof(WordNode));
strcpy(temp->Data,insertWord);
temp->Next=NULL;
move->Next=temp;
}
return root;
}
int Find_Equeal_Word(WordNode *a,WordNode*b){
int sum=0,pos;
int isV[MAX_VIS]={0};
WordNode *b_temp=(WordNode*)malloc(sizeof(WordNode));
b_temp=b;
while(a!=NULL){
pos=0;
while(b!=NULL){
if(!isV[pos] && !My_Stricmp(a->Data,b->Data)){
isV[pos]=1;
sum++;
break;
}
pos++;
b=b->Next;
}
a=a->Next;
b=b_temp;
}
return sum;
}
第三种做法:吸取上面的教训,但发现Insert的过程其实也在比较(虽然比数组好),但后面查询时候要从头遍历链表,太浪费时间,由此想到用哈希表来弄,减短查询时间,用的 EFLHash函数 + 线性探测法 解决碰撞冲突问题,这样后面就可以直接查找比较了(相同单词在相同位置,我是放弃了空间,省时间),但还是超时最后一个点,求大佬帮看看还能怎么优化!!!
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_TEXT 10001
#define MAX_WORD 100
void toUp(char *str);
unsigned int ELFHash(char *str);
typedef struct{
char Data[MAX_WORD][11];
int Article_Size;
int First_Pos;
} Article;
Article artice[MAX_TEXT];
char text[11];
int main(){
for(int i=0; i<MAX_TEXT; i++){
for(int j=0; j<MAX_WORD; j++){
artice[i].Data[j][0]=' ';
}
}
int n,firstPos;
scanf("%d",&n);
char ch;
for(int i=0; i<n; i++){
int j=0,size=0,begin=0,save=0;
firstPos=MAX_WORD;
getchar();
while((ch=getchar())!='#'){
if(('a'<=ch && ch<='z') || ('A'<=ch && ch<='Z')){
text[j++]=ch;
if(j==10){
text[j]='\0';
toUp(text);
int pos=ELFHash(text),flag=1;
while(artice[i].Data[pos][0]!=' '){
if(!strcmp(artice[i].Data[pos],text)){
flag=0;
break;
}
pos++;
if(pos==MAX_WORD){
pos=0;
}
}
if(flag){
if(pos<firstPos){
firstPos=pos;
}
strcpy(artice[i].Data[pos],text);
size++;
}
j=0;
while((ch=getchar())){
if(!(('a'<=ch && ch<='z') || ('A'<=ch && ch<='Z'))){
break;
}
}
}
}else{
if(j>=3){
text[j]='\0';
toUp(text);
int pos=ELFHash(text),flag=1;
while(artice[i].Data[pos][0]!=' '){
if(!strcmp(artice[i].Data[pos],text)){
flag=0;
break;
}
pos++;
if(pos==MAX_WORD){
pos=0;
}
}
if(flag){
if(pos<firstPos){
firstPos=pos;
}
strcpy(artice[i].Data[pos],text);
size++;
}
}
j=0;
}
}
artice[i].First_Pos=firstPos;
artice[i].Article_Size=size;
}
int m,a,b,equ,pos,end;
scanf("%d",&m);
for(int i=0; i<m; i++){
scanf("%d %d",&a,&b);
a--;b--;
equ=0;
for(int j=artice[a].First_Pos; j<MAX_WORD; j++){
if(artice[a].Data[j][0]==' '){
continue;
}
if(!strcmp(artice[a].Data[j],artice[b].Data[j])){
equ++;
}
}
int total=(artice[a].Article_Size-equ)+(artice[b].Article_Size-equ)+equ;
printf("%.1f%%\n",(double)equ/total*100);
}
return 0;
}
void toUp(char *str){
int move=0;
while(*(str+move)){
if('a'<=*(str+move) && *(str+move) <= 'z'){
*(str+move)-='a'-'A';
}
move++;
}
}
unsigned int ELFHash(char *str){
unsigned int hash = 0;
unsigned int x = 0;
while (*str){
hash = (hash << 4) + (*str++);
if ((x = hash & 0xF0000000L) != 0){
hash ^= (x >> 24);
hash &= ~x;
}
}
return (hash & 0x7FFFFFFF)%MAX_WORD;
}