问题 G: 单词检查

来源:互联网 发布:rar解压软件官网 编辑:程序博客网 时间:2024/06/01 20:10

题目描述

  • 技术要求:线性表或哈希表或AVL树或红黑树(创建字典的技术)

输入
i
is
has
have
be
my
more
contest
me
too
if
award
#

  • (以上为录入字典,此行为结束录入字典,即输入#回车后录入完毕)

me
aware
m
contest
hav
oo
or
i
fi
mre
#

  • (结束比较)

二、分析与设计

鄙人不才,AVL树旋转完全不会,红黑树也不会,哈希表才开始学,所以只能用被江湖朋友们笑话的线性表查找
首先设计接口
可知,此处使用链表技术创建字典,然后匹配有3种情况
完全正确(完全匹配)
不能匹配(差别太大)
模糊匹配(这里又分三种)

  1. CUT,剪辑匹配,用户输入的单词删减某一个字母能在字典上找到的
  2. Insert,插入匹配,用户输入的单词增加某一个字母能在字典上找到的
  3. replace,用户输入的单词替换某一个字母能在字典上找到的
    由此,单次操作的接口
/*以下为单词操作或者单词生成技术,bool判断是否完全匹配*/bool ReplaceMatch(const pNode DicPage, const char *word,char *result);bool CutMatch(const pNode DicPage, const char *word, char *result);bool InsertMatch(const pNode DicPage, const char *word, char *result);bool MatchFail ( const char *word);/*以上返回真或假,自带匹配成功后的输出函数*/

此处建立字典

//建立字典的函数,以下函数实参一律不准修改,只能修改函数里的参数int CreateDictonaty(pNode *head);//直接修改字典的头指针,也就是修改*headbool   Judge(const pNode DicHead, const char *word, char *result);//函数功能开始判断,使用哪种技术

**

设计思想:

**

  1. 分析设计Judge函数

字典录入完毕后,用户输入一个单词,就在字典里从链表最开头位置找,接着比较字典和用户单词的长度,判断使用之前三种中哪种匹配

DicLength = strlen(ptr->data);//字典长度        WordLength = strlen(word);//用户单词长度        //printf("1=%d,2=%d\n", DicLength, WordLength);    //  system("pause")//调试过程        if (!strcmp(ptr->data, word))            return true;//发现完全匹配情况需要单独列出            switch (DicLength - WordLength)//分四种情况输出结果            {            case 0:ReplaceMatch(ptr, word, result); break;            case 1: InsertMatch(ptr, word, result); break;            case -1:CutMatch(ptr, word, result); break;            default:MatchFail(word); break;            }

匹配任务的情况就这样划分完成了,这样也确保了函数使用的时候,用户和字典一定是符合函数使用规范的情况

  1. 接下来依次分析四种匹配函数
bool MatchFail( const char *word){    return true;}

显然此处如果相差长度绝对值大于1,那么肯定dismatch,返回false

bool CutMatch(const pNode DicPage, const char *word, char *result)

举例
此处字典页数(单词)已经确定(也就是到了链表的这个元素),(每删除后还原)用户单词从第一个到最后一个字母依次删除
如 字典是 tangshiloveguting
而 用户输入的是 taengshiloveguting(第三个多了e
此时程序执行是这样子的,
aengshiloveguting与tangshiloveguting
匹配失败,继续删改
tengshiloveguting与tangshiloveguting
匹配失败,继续删改
tangshiloveguting与tangshiloveguting
匹配成功,把该字典的单词录入结果等待输出
如果一直到删减到最后一个单词,还未找到合适的,那么返回此时的字典这一个单词并不是候选答案

bool InsertMatch(const pNode DicPage, const char *word, char *result)

这个方式和上面类似,也是从第一个字母之前开始插入,一直插入到最后一个字母的后面,若有相同的单词,那么返回成功找到并录入等待的输出结果,此处可以偷一下懒,插入就是把待插入的位置变成一个非英文字母的,然后放在替换查找里面

bool ReplaceMatch(const pNode DicPage, const char *word, char *result)
  • 替换查找的技术详解:

先设置不匹配次数和不匹配的位置

int DismatchTimes = 0;int DismatchPosition = 0;

每次发现不匹配时候,把它记录下来,并且不匹配次数++
不匹配的次数大于2的时候返回失败(题目要求)
次数为1的时候把它的不匹配的地方从字符 a换到z,若有一毛一样的,录入输出结果,然后函数返回真

三、性能分析

和我之前用顺序表(结构体数组)的时间复杂度比起来,大概是顺序表的十倍
时间复杂度考虑最坏的情况,是n*24*单词长度*,大概是O(n)

四、调试与测试数据

调试的时候总是发现奇奇怪怪的问题,后来发现是使用strcat有问题,空格总是不能正确输出,于是没办法,,代码又不够简洁了
输出的时候,发现还是要等字典翻阅完毕才能判断输出情况(这种比较情况可以确保输出结果是按照字典顺序)
遇到完美匹配情况,前面录入的结果也就没有了用处,这时候是不应该输出录入的无用结果的
回车符号要注意是否残留
OJ上总是格式错误我也没办法,烦心,最近把线索二叉树码了一遍,AVL树太麻烦了,学长说用树堆,也就是treap,我看了堆排序,看了树堆大概的运作方式,使用依然不是很会,放假的时候再去在程序员老爸的指导下学一学

下面是各个文件的完整源码

/*linkedlist.h,也就是接口文件*/
#include <stdio.h>#include <stdlib.h>#include<string.h>#define true 1#define false 0#define error -1#define maxsize 10#define bool int//真值表#ifndef __ts#define DATATYPE char *typedef  struct tsNode Node, *pNode;struct tsNode{    char data[15];//数据域,头的数据域用来存放长度    pNode          next;//指针域};#endif//有头结点的链表基础功能pNode Init_With_Head_linkedlist(void);//创建一个有头结点的空链表int append_tail_H(pNode head, DATATYPE value);//add node in the tailint append_front_H(pNode head, DATATYPE value);//add a node before the headint connect_linkedlist(pNode source, pNode dest);int Insert_front_linkedlist(pNode head, DATATYPE value, DATATYPE position);//只能插入在中间int delete_node(pNode head, DATATYPE position);int destory_linkedlist(pNode head);pNode find_linkedlist(pNode head, DATATYPE value);int modify_node(pNode head, DATATYPE value, DATATYPE position);int traverse_linkedlist(pNode head);pNode divide_linkedlist_H(pNode head);//函数实用性暂时不明DATATYPE get_node(pNode head, DATATYPE position);//建立字典的函数,以下函数实参一律不准修改,只能修改函数里的参数int CreateDictonaty(pNode *head);//直接修改字典的头指针,也就是修改*headbool   Judge(const pNode DicHead, const char *word, char *result);//开始判断,使用哪种技术/*以下为单词操作或者单词生成技术,bool判断是否完全匹配*/bool ReplaceMatch(const pNode DicPage, const char *word,char *result);bool CutMatch(const pNode DicPage, const char *word, char *result);bool InsertMatch(const pNode DicPage, const char *word, char *result);bool MatchFail ( const char *word);/*以上返回真或假,自带匹配成功后的输出函数*/
//此处是链表技术,出处嘛,虽然是看视频来的,但是还是自己调试过关了的#include"linkedlist.h"pNode Init_With_Head_linkedlist(void){    pNode phead = NULL;    phead = (pNode)malloc(sizeof(Node));    if (phead == NULL)    {        return phead;    }    phead->next = NULL;    //phead->data = -10;    return phead;}int append_tail_H(pNode head, DATATYPE value)//add node in the tail{    pNode node = (pNode)malloc(sizeof(Node));    if (node == NULL)    {        printf("内存分配失败\n");        system("pause");        return -1;    }    strcpy( &node->data[0],value);    node->next = NULL;    while (head->next != NULL)    {        head = head->next;    }    head->next = node;    return true;}int append_front_H(pNode head, DATATYPE value)//add a node before the head{    if (head == NULL)    {        return false;    }    pNode ptr = NULL;    ptr = (pNode)malloc(sizeof(Node));    strcpy(&ptr->data[0],value );    ptr->next = head->next;    head->next = ptr;//  head->data++;    return true;}int connect_linkedlist(pNode source, pNode dest)//两个有头的{    if (source == NULL)    {        return false;    }    while (source->next != NULL)    {        source = source->next;    }    source->next = dest->next;    free(dest);    return true;}int Insert_linkedlist(pNode head, DATATYPE value, DATATYPE position)//只能插入在中间{    position--;    while (position--)    {        head = head->next;        if (head->next == NULL) return false;    }    pNode pNew;    pNew->next == NULL;    strcpy(&pNew->data[0], value);//  pNew->data = value;    pNew->next = head->next;    head->next = pNew;    return true;}int delete_node(pNode head, DATATYPE position){    pNode pfree = NULL;    while (head->next->data != position)//带头节点的    {        head = head->next;        if (head->next->next == NULL) return false;    }    pfree = head->next;    head->next = head->next->next;    free(pfree);    return true;}DATATYPE get_node(pNode head, DATATYPE position){    while (head->next->data != position)//带头节点的    {        head = head->next;        if (head->next->next == NULL) return false;    }    return head->next->data;}int traverse_linkedlist(pNode head){    pNode ptr = head->next;    int count = 0;    while (ptr != NULL)    {        printf("the %d node->data=%s\n", count, ptr->data);        count++;        ptr = ptr->next;    }}int destory_linkedlist(pNode head){    if (head == NULL)    {        return -1;    }    else    {        destory_linkedlist(head->next);        free(head);        return 0;    }}pNode find_linkedlist(pNode head, DATATYPE value){    while (head->data != value)    {        if (head == NULL) return head;        head = head->next;    }    return head;}int modify_node(pNode head, DATATYPE value, DATATYPE position){    while ((head->data) != position)    {        if (head->next == NULL) return false;    head = head->next;    }    //head->data = value;    return true;}
//字典操作技术#include"linkedlist.h"/*修改建议单词可以采用如下生成技术:(1)在每一个可能位置插入‘a-'z'中的一者(2)删除单词中的一个字符(3)用‘a'-'z'中的一者取代单词中的任一字符大概的思路:首先用链表存储字典,数据域改成每个单词,每个单词的长度字典录入完毕然后就不用链表存东西了,浪费空间以#结束输入匹配有四种情况完美匹配增加字母匹配成功删减字母匹配成功替换字母匹配成功匹配阶段录入一个单词,直接开始暴力跑字典,匹配首先判断待 匹配字串长度 和 字典此时字串 长度,相差两个长度直接匹配失败输入:换行若待 匹配的 长于 字典此时字串 长度,那么执行删减匹配若长度相同,执行替换处理关于中止条件及其优化是技术上的事情,在详细代码里会有说明输出约束条件的解决方案,按字典顺序,如果按照上面的匹配,应该是没有问题的*///直接返回字典的头指针int CreateDictonaty(pNode *MyDictionary){    char * buffer;    buffer = (char *)malloc(sizeof(char)* 25);    buffer[0] = 'T';    //pNode MyDictionary=(pNode)malloc(sizeof(Node));    *MyDictionary = Init_With_Head_linkedlist();//初始化字典    while (gets(buffer), buffer[0] != '#')    {        append_tail_H(*MyDictionary, buffer);    }    return true;}//开始判断,使用哪种技术,字典的字一个个进入bool   Judge(const pNode DicHead, const char *word, char *result){    //int status = 0;    //printf("%s ", word);    int DicLength, WordLength;    //pNode pointer = DicHead->next;//用来判断是否存在完全匹配    pNode ptr = DicHead->next;//我这个版本的头是没用的    while (ptr != NULL)//字典没翻阅完毕    {        DicLength = strlen(ptr->data);        WordLength = strlen(word);        //printf("1=%d,2=%d\n", DicLength, WordLength);    //  system("pause")        if (!strcmp(ptr->data, word))            return true;//发现完全匹配情况            switch (DicLength - WordLength)//分四种情况输出结果            {            case 0:ReplaceMatch(ptr, word, result); break;            case 1: InsertMatch(ptr, word, result); break;//字典长了就向目标文字增加字            case -1:CutMatch(ptr, word, result); break;            default:MatchFail(word); break;            }        ptr = ptr->next;//字典翻页    }    return false;}bool MatchFail( const char *word){    return true;}bool CutMatch(const pNode DicPage, const char *word, char *result){    //char ch = 'a',tmp;//tmp存删去的字母    char WrongWord[25],NewWord[25];    strcpy(WrongWord, word);    int CutPosition = 0;    int len = strlen(WrongWord);    while (CutPosition<=len)//从第一个开始删除错误单词的字母    {        int status = 1;        //tmp = WrongWord[CutPosition];        int i = CutPosition;        while (WrongWord[i] != '\0')//删除之后得到的新字向左移动        {            WrongWord[i] = WrongWord[i + 1];            ++i;        }        WrongWord[i] = WrongWord[i + 1];// \0复制过来        status=strcmp(WrongWord, DicPage->data);//返回0时成功        if (!status)        {            int len2 = strlen(WrongWord);            WrongWord[len2 + 1] = '\0';            WrongWord[len2] = ' ';            //printf("\n%s\n", WrongWord);            strcat(result, WrongWord);        /*  int     strlen()*/        }//成功时存起来        //判断结束后还原        strcpy(WrongWord, word);        CutPosition++;//删除下一个字母    }    return false;}bool InsertMatch(const pNode DicPage, const char *word, char *result){    char WrongWord[25];    strcpy(WrongWord, word);    int InsertPosition = 0;    int len = strlen(word);    while (InsertPosition<=len)//从第一个开始选择一个地方来准备放入字母    {        //int status = 0;        int i =len;        while (i >=InsertPosition)//新字挪出空位向右,并且置换成NUL        {            WrongWord[i+1] = WrongWord[i];//从\0开始右移            --i;        }        WrongWord[InsertPosition] = 0; //置换成NUL    /*  printf("----------\n%s\n", WrongWord);*/        /*system("pause");*/        ReplaceMatch(DicPage, WrongWord,result);//腾出来的地方肯定算一个不匹配的地方,所以直接调用这个函数就好        strcpy(WrongWord, word);//判断结束,重新再来        InsertPosition++;//插入地方到下一个字母    //  printf("\n----------------------\n");    }    return false;}bool ReplaceMatch(const pNode DicPage, const char *word, char *result){    int DismatchTimes = 0;    int DismatchPosition = 0;    int i = 0;//仅作为下标    while (DismatchTimes <= 1 && DicPage->data[i] != '\0')    {        if (word[i] != DicPage->data[i])//发现一个字母不匹配就记录下来        {            DismatchPosition = i;            DismatchTimes++;        }        ++i;    }    if (DismatchTimes >= 2)        return 0;    //if (!DismatchTimes)   /*printf("is correct");*///全部匹配成功返回真(这里到judge函数里去弄)    //  return true;    char ch = 'a';//单个不匹配的地方从a到z换    while (ch<='z')    {        if (ch == DicPage->data[DismatchPosition])//发现错的字母部分替换这个字典该页有        {//因为此处不可能是完美匹配,所以存在多个待选正确单词            char WrongWord[25];            strcpy(WrongWord, DicPage->data);            int i = strlen(DicPage->data);            WrongWord[i + 1] = WrongWord[i];            WrongWord[i] = ' ';            //printf("\n%s\n\n", WrongWord);            strcat(result, WrongWord);            return true;        }        ch++;    }    return false;}
//主函数#include"linkedlist.h"int main(void){    pNode dic=NULL;    CreateDictonaty(&dic);    //traverse_linkedlist(dic);测试成功    char word[25];    while (gets(word), word[0] != '#')    {        int status = 0;        char result[10000] ;         result[0] = '\0' ;        status=Judge(dic, word, result);        if (status)            printf("%s is correct\n", word);        else        {            int len = strlen(result);            if (len!=0)            result[len - 1] = '\0';            printf("%s: %s\n", word, result);        }    }    //system("pause");    return 0;}