• 数据结构第四章总结


    数据结构第四章学习的是串,数组和广义表(广义表课程中没讲,问题不大)

    串的定义其实在c++学习中就有所接触,所以这里不详说,  重点说的是两个串模式匹配算法

    1.BF算法

    BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;

    若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

    2.KMP算法

    KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。

    具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。

    应用到PTA上的是

    7-1 串的模式匹配 (30 分)
     

    给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串S中出现的位置。

    输入格式:

    输入有两行: 第一行是主串S; 第二行是模式T.

    输出格式:

    输出相匹配的子串中的第一个字符在主串S中出现的位置。若匹配失败,输出0.

    输入样例:

    在这里给出一组输入。例如:

    aaaaaba
    ba
    

    输出样例:

    在这里给出相应的输出。例如:

    6
    KMP算法的代码实现运用到此题
    int Index_KMP(SString S, SString T, int next[]) { //S主串,T模式串
        int i = 1, j = 1;       //i指主串,j指模式串
        int result = 0;
        while (i <= S.length&&j <= T.length) {
            if (j == 0 || S.ch[i] == T.ch[j]) {     /如果匹配接着往下比较,不匹配就右移T
                i++;
                j++;
            }
            else {
                j = next[j];
            }
        }
        if (j > T.length) {     //当j到最后仍与主串相等+1时匹配成功
            result = i - T.length;
        }
        return result;
    }

    主要还是getnext函数

    void getNext(SString T, int next[]) {
        int i = 1, j = 0;   
        next[1] = 0;
        while (i < T.length) {
            if (j == 0 || T.ch[i] == T.ch[j]) { 
                i++;
                j++;
                if (T.ch[i] != T.ch[j]) {   //i和j都右移后如果不同,把最长串最后一个的位置给next
                    next[i] = j;
                }
                else {          //但是如果相同,就让next变为next[j]        
                    next[i] = next[j];
                }
            }
            else {              //如果j不为0且ch[i] 与 ch[j]不等,表示之前有相同串,但右移后又不同了  
                j = next[j];
            }
        }
      
    }

    最后则是学习到特殊矩阵的压缩存储

    运用到例题有

    7-1 稀疏矩阵 (30 分)
     

    如果一个矩阵中,0元素占据了矩阵的大部分,那么这个矩阵称为“稀疏矩阵”。对于稀疏矩阵,传统的二维数组存储方式,会使用大量的内存来存储0,从而浪费大量内存。为此,可以用三元组的方式来存放一个稀疏矩阵。

    对于一个给定的稀疏矩阵,设第r行、第c列值为v,且v不等于0,则这个值可以表示为 <。这个表示方法就称为三元组。那么,对于一个包含N个非零元素的稀疏矩阵,就可以用一个由N个三元组组成的表来存储了。

    如:{<1, 1, 9>, <2, 3, 5>, <10, 20, 3>}就表示这样一个矩阵A:A[1,1]=9,A[2,3]=5,A[10,20]=3。其余元素为0。

    要求查找某个非零数据是否在稀疏矩阵中,如果存在则输出其所在的行列号,不存在则输出ERROR。

    输入格式:

    共有N+2行输入: 第一行是三个整数m, n, N(N<=500),分别表示稀疏矩阵的行数、列数和矩阵中非零元素的个数,数据之间用空格间隔; 随后N行,输入稀疏矩阵的非零元素所在的行、列号和非零元素的值; 最后一行输入要查询的非0数据k。

    输出格式:

    如果存在则输出其行列号,不存在则输出ERROR。

    输入样例:

    在这里给出一组输入。例如:

    10 29 3
    2 18 -10
    7 1 98
    8 10 2
    2
    

    输出样例:

    在这里给出相应的输出。例如:

    8 10
    代码如下:
    #include<iostream>
    using namespace std; 
    
    typedef struct
    {
        int i;//第i行
        int j;//第j行
        int v;//第i行第j列的值 
    }Mnode;
    
    typedef struct
    {
        Mnode a[520];
        int length;//矩阵中非零元素的个数
         
    }Tuple;
    
    int main()
    {
        Tuple T;
        
        int m,n,N;//m,矩阵行数,n,矩阵列数,N,矩阵的非零元素个数 
        cin>>m>>n>>N;
        
        T.length=N;
    
        for(int i=0;i<T.length;i++)
        {
            cin>>T.a[i].i;    //输入非零元素所在行,列号和非零元素的值 
            cin>>T.a[i].j;
            cin>>T.a[i].v;    
        } 
             
        int d; 
        cin>>d;//输入查询数据
        
        if(d!=0)
        {
            for(int i=0;i<T.length;i++)
        {
            if(T.a[i].v == d)
            {
                cout<<T.a[i].i<<" ";
                cout<<T.a[i].j;
            }
            
            if( (T.a[i].v != d) && (i == T.length-1))
                cout<<"ERROR";
            
            else i++;
        
        }
        }
    
     } 

    收获:

    本章内容较多,尤其是学习到两个新接触的算法,KMP算法带来的新思想对理解能力是一个挑战,

    还有的是老师上课时带我们一步步去完成AI核心代码这道实践题,一步一步的编程思路可谓将问题

    需要注意的细节都能展现在代码上,滴水不漏。也是一个编程基本功的体现,下来之后又重新按照老师的

    算法思路一步步去完成,并加上老师还没讲到的can you 的改写,实现的代码:

    7-2 AI核心代码 (30 分)
    本题要求你实现一个简易版的 AI 英文问答程序,规则是:
    
    无论用户说什么,首先把对方说的话在一行中原样打印出来;
    消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
    把原文中所有大写英文字母变成小写,除了 I;
    把原文中所有独立的 I 和 me 换成 you;
    把原文中所有的问号 ? 换成惊叹号 !;
    把原文中所有独立的 can you 换成 I can —— 这里“独立”是指被空格或标点符号分隔开的单词;
    在一行中输出替换后的句子作为 AI 的回答。
    输入格式:
    输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。
    
    输出格式:
    按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。
    输入样例:
    在这里给出一组输入。例如:
    
    6
    Hello ?
     Good to chat   with you
    can   you speak Chinese?
    Really?
    Could you show me 5
    What Is this prime? I,don 't know
    输出样例:
    在这里给出相应的输出。例如:
    
    Hello ?
    AI: hello!
     Good to chat   with you
    AI: good to chat with you
    can   you speak Chinese?
    AI: I can speak chinese!
    Really?
    AI: really!
    Could you show me 5
    AI: could you show you 5
    What Is this prime? I,don 't know
    AI: what Is this prime! you,don't know
    
    
    
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    string s;
     void go(string s);
     bool isSeparator(char ch);
     bool isPunctuation(char eh);
     bool iscanyou(char t[],int j) ;
    int main()
    {
        string s;
        int n, i;
        cin >> n;
        getchar(); //吸收回车符 
        for(i=1; i<=n; ++i){
            getline(cin, s);
            cout << s << endl;
            cout << "AI: ";
            go(s);//AI根据s输出对话    
        }
        return 0;
    }
    
    void go(string s)
    {
        char t[3004];//输入全部是I的时候,输出长度是输入的三倍
        //string t;  
    
        int i, j;//i,j分别为s,t的下标
        
        //i定位到s的第一个非空字符 
        for(i=0; s[i]==' '; ++i);//循环体为空
    
        j = 0;     //j的初值为0
        
        //从s的第一个非空字符开始,逐个扫描,分情况复制到t
        while(s[i]!=''){
            if(s[i]==' ' && s[i-1]==' '){
                ++i; //如果漏了这句,有连续空格时会死循环
                continue;
            }            
            if(s[i]=='?'){
                t[j++] = '!';
                ++i;
                continue;
            }
            if(s[i]!='I'){
                t[j++] = tolower(s[i++]);
                continue;
            }
            t[j++] = s[i++]; //s[i]='I'的情况 
         }  
         
         t[j] = ''; //给t补上结尾符
            
            j=0;
        while(t[j]!=''){//处理单独I变成you,最主要还是isSeparator函数的编写 
             if(t[j]=='I' && (j==0 || isSeparator(t[j-1])) && isSeparator(t[j+1])){
                 cout << "you";
                 ++j;
                 continue;
             }
             //同理,me与I一样做法 
             if(t[j]=='m' && t[j+1]=='e' && (j==0 || isSeparator(t[j-1])) && isSeparator(t[j+2])){
                 cout << "you";
                 j += 2;
                 continue;
             }
             if(t[j]==' ' && isPunctuation(t[j+1])){//空格的下一个是标点,不输出 
               
                 ++j;
                continue;
     } 
             if(iscanyou(t,j))
             {
                 cout<<"I can";
                 j+=7;
                 continue;
             }
             
             if(s[j]==' '&&s[j+1]=='')
            {//处理结尾剩余得一个空格
                   ++j;
                continue;
            }   
            
            cout<<t[j++]; 
             } 
             cout<<endl;
      }     
    bool isSeparator(char ch)
    {  //判断ch是否分隔符 
      //ch可能是:数字、字母、标点、空格、
      //分隔符:!(数字、小写字母、I)
         ch = tolower(ch); 
         if(ch>='0' && ch<='9' || ch>='a' && ch<='z' || ch=='I')
            return false;
         else 
            return true;    
    }
    bool isPunctuation(char eh)
    {//判断空格下一个是标点,不输出
    
        eh=tolower(eh);
        if(eh>='0'&&eh<='9'||eh>='a'&&eh<='z'|| eh=='I' || eh==' ')
            return false;
        else
            return true;
    
    } 
    bool iscanyou(char t[],int j)
    { //处理can you的函数 
        if(t[j]=='c'&&t[j+1]=='a'&&t[j+2]=='n'&&t[j+3]==' '&&t[j+4]=='y'&&t[j+5]=='o'&&t[j+6]=='u')
        {
            if((j==0||isSeparator(t[j-1]) )&&isSeparator(t[j+7]))
                return true; 
        } 
        else    
          return false;
                
    }    
             
         

    自己的编程能力有了一定的提高,但还要继续努力,达到自己理想中的状态。

    希望自己能多点实践上机,好好巩固一下第四章学到的东西,而第五章将要学的树和二叉树

    也要努力地跟上呀!

    最后还是推荐一下学者网上老师发布的KMP算法的解析,看完之后其实也颇有收获的

  • 相关阅读:
    C# ViewState
    ASP.NET C#中Application Session 的用法
    JS取 Input 控件值方法
    JS通过HTML标签自身属性获取属性值
    SQLServer 数据库操作
    查看和修改 Windows 实例远程桌面默认端口
    只能输入数字0-9 正则表达式,兼容Google Firefox IE浏览器
    C# 类初始化顺序
    window系统 任务计划程序
    微信公众号开发--消息接收与回复
  • 原文地址:https://www.cnblogs.com/fengwanthousand/p/10708000.html
Copyright © 2020-2023  润新知