《大话数据结构》第五章:串(笔记)

第五章:串

    • 串(string): 由零个或多个字符组成的有限序列,也叫字符串。
  • 一、常见编码:
  • 二、 操作Index的实现算法
  • 三、朴素的模式匹配算法
  • 四、KMP模式匹配算法
  • 五、string中常用函数
    • 1. string的构造函数的形式:
    • 2.string的大小和容量:
    • 3.string的字符串比较:
    • 4.string的插入:push_back() 和 insert()
    • 5.string拼接字符串:append()、+ 操作符:
    • 6.string的删除:erase()
    • 7.string的字符查找与替换:
    • 8.string的排序:sort(s.begin(),s.end())
    • 9.string的大小写转换:tolower()和toupper()函数 或者 STL中的transform算法
      • 1)使用C语言中的函数方法:
      • 2)通过STL的transform算法配合的toupper和tolower来实现该功能
  • 六、C标准库——

串(string): 由零个或多个字符组成的有限序列,也叫字符串。

  • 空串(null string):零个字符串,不包含字符的串,长度为零,用引号 ‘’ 或 ”Φ“ 表示,引号不属于串的内容。
  • 空格串:’ ',只包含空格的串,空格串是有长度的,空格越多表示的串的越长。

一、常见编码:

ASCII:(American Standard Code for Information Interchange)计算机常用字符标准编码码,用7位二进制数表示一个字符,总共可表示128个字符,后拓展ASCII码由8位二进制表示一个字符,总共可以表示256个字符。

Unicode: 统一码、万国码或单一码,通常用2个字节,16位二进制表示一个字符,总共可表示2^16幂个字符,前256个字符和ASCII码兼容。

UTF-n:(Unicode Transformation Format),正对Unicode的一种可变长度字符编码,UTF-8:最小使用8个二进制编码(1个字节编码或2或3字节编码),UTF-16:最小使用16个二进制编码(2个字节或4个字节)其都是Unicode编码方案。

二、 操作Index的实现算法

利用串的一些自定义函数方法,比较字符串string1中是否有与string2相同的子串,如有则返回在pos后第一次出现相同子串的位置:

def str_length(string):  # 利用循环读数,获取字符串长度
    length = 0
    for i in string:
        if i != '':
            length += 1
    return length


def get_substring(str1, pos, length):  # 获取固定位置后定长子串
    l1 = str_length(str1)
    if 0 <= length <= l1 - pos + 1:
        substring = str1[(pos - 1):(pos + length - 1)]
        return substring


def comp_string(str1, str2):  # 利用循环字符串每一位是否相同
    l1 = str_length(str1)
    l2 = str_length(str2)
    if l1 == l2:
        count = 0
        for i in range(l1):
            if str1[i] == str2[i]:
                count += 1
            else:
                break
        if count == l1:
            return 1
        else:
            return -1
    else:
        return -1


def index(string1, string2, pos):  # 比较string1中是否有string2子串,如有则返回pos后第一次出现位置
    l1 = str_length(string1)
    l2 = str_length(string2)
    pos_temp = pos
    while 0 <= pos_temp <= l1 - l2 + 1:
        sub = get_substring(string1, pos_temp, l2)
        result = comp_string(sub, string2)
        if result > 0:
            return '字符串\'{}\'与字符串\'{}\',在位置{}后第一次相同位置为{}'.format(string1, string2, pos, pos_temp)
        else:
            pos_temp += 1
    if pos_temp > l1 - l2 + 1:
        return '字符串\'{}\'与字符串\'{}\'无相同子串'.format(string1, string2)


if __name__ == '__main__':
    test_a = 'abcedabcdeddffee'
    test_b = 'ddff'
    test_c = 'ddfg'
    pos = 4

    print(index(test_a, test_b, pos))

当然利用python中的len(),compare()就可获取字符串长度,比对字符串是否相同,为了更清楚理解其原理,改用自定义函数。

三、朴素的模式匹配算法

模式匹配: 在子串中的定位操作,不考虑串的其他操作,仅用基本的字符遍历寻找子串位置:

def index(string1, string2, pos):
    l1 = len(string1)
    l2 = len(string2)
    pos_temp = pos
    j = 0
    while l1 - l2 - pos_temp + 1 >= 0:
        if string1[pos_temp] == string2[j]:
            pos_temp += 1
            j += 1
        else:
            pos_temp += 1
            j = 0

    if j >= l2:
        pos_temp = pos_temp - l2 + 1
        return '字符串\'{}\'与字符串\'{}\',在位置{}后第一次相同位置为{}'.format(string1, string2, pos, pos_temp)

    else:
        return '字符串\'{}\'与字符串\'{}\'无相同子串'.format(string1, string2)


if __name__ == '__main__':
    test_a = 'abcedabcdeddffee'
    test_b = 'ddff'
    test_c = 'ddfg'
    pos = 4
    print(index(test_a, test_b, pos))

时间复杂度:O[(L1-L2+1) * L2]

四、KMP模式匹配算法

由克努特—莫里斯—普拉特(D.E.Knuth,J.H.Morris,V.R.Pratt)提出的KMP模式匹配算法,根据匹配子串结构重复情况,在第一次匹配后减少不必要的重复性比对过程。根据子串结构得出next[]数组:

《大话数据结构》第五章:串(笔记)_第1张图片

next[] 数组原表示当前位置匹配前缀与匹配后缀的长度,以下程序get_next() 获取当前位置字符串匹配过程中不相等时需要回溯的位置,get_nextval() 作为next的一种升级为降低特殊子串,如’aaaaax’的计算复杂度,详细推导过程见《大话数据结构》p142
Python代码实现:

def get_next(string):  # 计算当前字符串的next数组
    length = len(string)
    next = []
    next.append(0)
    i = 1
    j = 0
    while i < length:
        if j == 0 or string[i - 1] == string[j - 1]:
            i += 1
            j += 1
            next.append(j)
        else:
            j = next[j - 1]  # 字符不相等,j值回溯
    return next


def get_nextval(string):
    l1 = len(string)
    j = 0
    i = 1
    nextval = []
    nextval.append(0)
    while i < l1:
        if j == 0 or string[i - 1] == string[j - 1]:
            j += 1
            i += 1
            if string[i - 1] != string[j - 1]:
                nextval.append(j)
            else:
                nextval.append(nextval[j - 1])
        else:
            j = nextval[j - 1]
    return nextval


def index_kmp(string1, string2, pos):
    i = pos
    j = 1
    next = get_next(string2)  # 获取next数组
    # next = get_nextval(string2)  # 获取升级后的nextval数组
    l1 = len(string1)
    l2 = len(string2)
    while i < l1 and j < l2:
        if j == 0 or string1[i - 1] == string2[j - 1]:
            j += 1
            i += 1
        else:
            j = next[j - 1]  # next数组,回溯j值
    if j >= l2:
        return '字符串{}与子串{},在{}后第一次相同位置为{}'.format(string1, string2, pos, i + 1 - l2)
    else:
        return 'No match substring!'


if __name__ == '__main__':
    test_a = 'abcedabcdeddffee'
    test_b = 'ddff'
    test_c = 'aaaabcde'
    test_d = 'aaaax'
    pos = 4
    print(index_kmp(test_a, test_b, pos))
    # print(index_kmp(test_c, test_d, pos))

KPM 模式匹配算法作为理解 index 函数有着重要作用。
C++代码实现:

#include 
using namespace std;

//KMP算法查找匹配字符串

void get_next(string T, int *next) //获取next数组
{
     
	int i = 1;
	int j = 0;
	next[i] = 0;

	while (i < T[0])
	{
     
		if (j == 0 || T[i] == T[j])
		{
     
			i++;
			j++;
			next[i] = j;
		}
		else
			j = next[j];
	}
}

//改进后的next数组
void get_nextval(string T, int* nextval)
{
     
	int i = 1;
	int j = 0;
	nextval[i] = 0;
	while (i < T[0])
	{
     
		if (j == 0 || T[i] == T[j])
		{
     
			i++;
			j++;
			if (T[i] != T[j])
				nextval[i] = j;
			else
				nextval[i] = nextval[j];
		}
		else
			j = nextval[j];
	}
}

五、string中常用函数

1. string的构造函数的形式:

string str:生成空字符串
string s(str):生成字符串为str的复制品
string s(str, strbegin,strlen):将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值
string s(cstr, char_len):以C_string类型cstr的前char_len个字符串作为字符串s的初值
string s(num ,c):生成num个c字符的字符串
string s(str, stridx):将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值

2.string的大小和容量:

size()length():返回string对象的字符个数,他们执行效果相同。
max_size():返回string对象最多包含的字符数,超出会抛出length_error异常
capacity():重新分配内存之前,string对象能包含的最大字符数

3.string的字符串比较:

  • int compare(const string &s) const; //与字符串s比较
  • int compare(const char *s) const; //与字符串s比较
= 返回   0
> 返回   1 
< 返回  -1

4.string的插入:push_back() 和 insert()

  • string& insert(int pos, const char* s); //插入字符串
  • string& insert(int pos, const string& str); //插入字符串
  • string& insert(int pos, int n, char c); //在指定位置插入n个字符c
void test4()
{
     
	string str = "hello";
		str.insert(1, "111");
	
	string s1;
	    // 尾插一个字符
	    s1.push_back('a');
	    s1.push_back('b');
	    s1.push_back('c');
}

5.string拼接字符串:append()、+ 操作符:

void test5()
{
     
    // 方法一:append()
    string s1("abc");
    s1.append("def");
    cout<<"s1:"<<s1<<endl; // s1:abcdef

    // 方法二:+ 操作符
    string s2 = "abc";
    /*s2 += "def";*/
    string s3 = "def";
    s2 += s3.c_str();
    cout<<"s2:"<<s2<<endl; // s2:abcdef
}

6.string的删除:erase()

string& erase(int pos, int n = npos); //删除从Pos开始的n个字符

void test6()
{
     
    string s1 = "123456789";

    s1.erase(s1.begin()+1);              // 结果:13456789
    s1.erase(s1.begin()+1,s1.end()-2);   // 结果:189
    s1.erase(1,6);                       // 结果:189
}

7.string的字符查找与替换:

功能描述:

  • 查找:查找指定字符串是否存在
  • 替换:在指定的位置替换字符串

函数原型:

  • int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找
  • int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找
  • int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
  • int find(const char c, int pos = 0) const; //查找字符c第一次出现位置
  • int rfind(const string& str, int pos = npos) const; //查找str最后一次位置,从pos开始查找
  • int rfind(const char* s, int pos = npos) const; //查找s最后一次出现位置,从pos开始查找
  • int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符最后一次位置
  • int rfind(const char c, int pos = 0) const; //查找字符c最后一次出现位置
  • string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str
  • string& replace(int pos, int n,const char* s); //替换从pos开始的n个字符为字符串s
void test01()
{
     
	//查找
	string str1 = "abcdefgde";

	int pos = str1.find("de");

	if (pos == -1)
	{
     
		cout << "未找到" << endl;
	}
	else
	{
     
		cout << "pos = " << pos << endl;
	}	

	pos = str1.rfind("de");
	cout << "pos = " << pos << endl;
}

void test02()
{
     
	//替换
	string str1 = "abcdefgde";
	str1.replace(1, 3, "1111");

	cout << "str1 = " << str1 << endl;
}

8.string的排序:sort(s.begin(),s.end())

#include 

void test8()
{
     
    string s = "cdefba";
    sort(s.begin(),s.end());
    cout<<"s:"<<s<<endl;     // 结果:abcdef
}

9.string的大小写转换:tolower()和toupper()函数 或者 STL中的transform算法

1)使用C语言中的函数方法:

#include 
#include 
using namespace std;

int main()
{
     
    string s = "ABCDEFG";

    for( int i = 0; i < s.size(); i++ )
    {
     
        s[i] = tolower(s[i]); //转换为小写
    }

    cout<<s<<endl;
    return 0;
}

2)通过STL的transform算法配合的toupper和tolower来实现该功能

test2()
{
     
	string s = "ABCDEFG";
	string result;
	result.resize(s.size()); //不可缺,提前预留空间

	transform(s.begin(), s.end(), result.begin(), tolower); //将转换后的结果存入result

	transform(s.begin(), s.end(), s.begin(), tolower); //直接将s转化为小写

	cout << "result:" << result << endl;
	cout << "s:" << s << endl;
}

六、C标准库——

常见函数接口:

包含头文件

序号 函数&描述
1 int isalnum(int c),该函数检查所传的字符是否是字母和数字。
2 int isalpha(int c),该函数检查所传的字符是否是字母。
3 int isdigit(int c),该函数检查所传的字符是否是十进制数字。
4 int islower(int c),该函数检查所传的字符是否是小写字母。
5 int isupper(int c),该函数检查所传的字符是否是大写字母。
6 int isspace(int c),该函数检查所传的字符是否是空白字符。

你可能感兴趣的:(大话数据结构,CPP,算法,python,字符串)