美文网首页设计模式
《算法与数据结构 C语言描述》第三章 字符串

《算法与数据结构 C语言描述》第三章 字符串

作者: cain_huang | 来源:发表于2018-08-26 20:43 被阅读65次

    3.1 字符串及其抽象数据类型

    3.1.1 基本概念

    字符串简称串,是一种特殊的线性表,其特殊性主要在于表中的每个元素是一个字符。
    一个串可以记为 s="s0s1..sn-1"(n >= 0),其中s是串的名字,双引号括起来的字符序列是串的值。

    3.1.2 抽象数据类型

    ADT String is
    operations
        String createNullStr(void) // 创建一个空串
        int isNullStr(String s) // 判断串s是否为空
        int length(String s) // 返回s的长度
        String concat(String s1, String s2) // 返回将串s1和串s2拼接在一起构成的一个新串
        String subStr(String s, int i, int j) // 在串s中,求从串的第i个字符开始连续j个字符所构成的子串
        int idnex(String s1, String s2)  // 如果串s2是s1的子串,则可求串s2在串s1中的第一次出现的位置
    end ADT String
    

    3.2 字符串的实现

    3.2.1 顺序表示

    顺序串类型定义如下:

    typedef struct SeqString {
        int MAXNUM;
        int n;
        char *c;
    } SeqString;
    

    创建空顺序串

    SeqString *createNullStr_Seq(int m) {
        SeqString *pstr = (SeqString *) malloc(sizeof(SeqString));
        if (pstr != NULL) {
            pstr->c = (char *) malloc(sizeof(char) * m);
            if (pstr->c) {
                pstr->n = 0;
                pstr->MAXNUM = m;
                return pstr;
            } else {
                free(pstr);
            }
        }
        return NULL;
    }
    

    求顺序表示的串的子串

    SeqString *substr_seq(SeqString *s, int i, int j) {
        SeqString *s1;
        int k;
        s1 = createNullStr_seq(j);
        if (s1 == NULL) {
            return NULL;
        }
        if (i > 0 && i <= s->n && j > 0) {
            if (s->n < i+j -1) {
                j = s->n - i + 1;
            }
            for (k = 0; k < j; k++) {
                s1->c[k]= s->c[i+k-1];
            }
            s1->n = j;
        }
        return s1;
    }
    

    3.2.2 链接表示

    链表定义如下:

    typedef struct StrNode {
        char c;
        struct StrNode * link;
    } StrNode;
    typedef struct StrNode *LinkString;
    

    创建带头结点的空链串

    LinkString createNullStr_link(void) {
        LinkString pst;
        pst = (LinkString) malloc(sizeof(StrNode));
        if (pst != NULL) {
            pst->link = NULL;
        }
        return pst;
    }
    

    求单链表示的串的子串

    LinkString subStr_link(LinkString s, int i, int j) {
        LinkString s1;
        StrNode *p, *q, *t;
        int k;
        s1 = createNullStr_link();
        if (s1 == NULL) {
            return NULL;
        }
        if (i < 1 || j < 1) {
            return s1;
        }
        p = s;
        for (k = 1; k <= i; k++) [
            if (p != NULL) {
                p = p->link;
            } else {
                return s1;
            }
        }
        if (p == NULL) {
            return s1;
        }
        t = s1;
        for(k = 1; k <= j; k++) {
            if (p != NULL) {
                q = (StrNode *) malloc(sizeof(StrNode));
                if (q == NULL) {
                    return s1;
                }
                q->c = p->c;
                q->link = NULL;
                t->link = q;
                t = q;
                p = p->link;
            }
        }
        return s1;
    }
    

    3.3 模式匹配

    3.3.1 朴素的模式匹配

    基本思想

    实现模式匹配的最简单做法是:用p中的字符串依次与t中的字符比较。如下图(a)所示,如果t0 = p0, t1 = p1, ..., tm-1 = pm-1,则匹配成功,返回第一次出现的位置是从第一个字符t0开始。否则必有某个i(0<= i <= m-1),使得 ti != pi,这是可将p右移一个字符,重新开始比较。

    朴素的模式匹配

    例子如下:


    朴素的模式匹配举例

    朴素的模式匹配算法

    /**
     * t为主串,p为匹配串
     */
    int index(SeqString *t, SeqString *p) {
        int i, j;
        i = 0;
        j = 0;
        while(i < p->n && j < t->n) {
            if (p->c[i] == t->c[j]) { // 继续匹配下一个字符
                i++;
                j++;
            } else { // 主串、子串回溯,重新下一次匹配
                j = j - i + 1;
                i = 0;
            }
        }
        if (i >= p->n) {
            return j-p->n+1; // 返回p中低一个字符在t中的序号
        } else {
            return -1; // 匹配失败返回-1
        }
    }
    

    缺点:效率不高

    3.3.2 无回溯的模式匹配

    基本思想

    造成朴素匹配算法速度慢的原因是有回溯,而这些回溯不一定是必要的。
    为了提高提配的速度,要能找到一个大于等于1的右移的位数,并且确定p和t继续比较的字符。最为理想的情况,匹配过程对于t是无回溯的。也就是在右移若干位后,应该立即用p中一个新的字符pk(k < i) 和 tj(甚至tj+1)继续进行比较。

    无回溯的模式匹配算法

    int pMatch(SeqString *t, SeqString *p, int *next) {
        int i, j;
        i = 0, j = 0;
        while (i <p->n && j <t->n) {
            if (i == -1 || p->c[i] == t->c[j]) {
                i++; j++;
            } else {
                i = next[i]; 
            }
        }
        if (i >= p->n) {
            return j - p->n + 1; // 匹配成功
        } else {
            return -1; // 匹配失败
        }
    }
    

    计算next数组

    makeNext(SeqString *p, int *next) {
        int i = 0, k = -1;
        next[0] = -1;
        while (i < p->n - 1) {
            while(k >=0 && p->c[i] != p->c[k]) {
                k = next[k];
            }
            i++;
            k++;
            next[i] = k;
        }
    }
    

    计算next数组(改进后)

    makeNext(SeqString *p, int *next) {
        int i = 0, k = -1;
        next[0] = -1;
        while (i < p->n - 1) {
            while (k >= 0 && p->c[i] != p->c[k]) {
                k = next[k];
            }
            i++;
            k++;
            if (p->c[i] == p->c[k]) {
                next[i] = next[k];
            } else {
                next[i] = k;
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:《算法与数据结构 C语言描述》第三章 字符串

        本文链接:https://www.haomeiwen.com/subject/txmmpftx.html