【数据结构学习笔记】字符串

一、基础知识

1. 字符串与字符变量:字符串每一个位置上的元素都是一个字符变量。Python 使用 str 对象来代表字符串,为一种不可变类型对象。在定义之后无法更改字符串的长度,也无法改变或删除字符串中的字符

2. 字符串的比较:

        1)依次比较对应位置上的字符编码大小,可以用ord()函数来返回对应字符的ascii码来实现

        2)若前几位字符都相同,则比较长度(误区:不能先比较长度!)

3. 存储方法

        1)顺序存储:根据下标访问 s[i]

        2)链式存储:字符串的链节点包含一个用于存放字符的 data 变量,和指向下一个链节点的指针变量 next

        通常情况下,链节点的字符长度为 1 或者 4,这是为了避免浪费空间。当链节点的字符长度为 4 时,由于字符串的长度不一定是 4 的倍数,因此字符串所占用的链节点中最后那个链节点的 data 变量可能没有占满,我们可以用 # 或其他不属于字符集的特殊字符将其补全

        

代码实现

1. 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

class Solution:
    def isPalindrome(self, s: str) -> bool:
        i=0
        j=len(s)-1
        while (i<len(s))&(j>=0):
            if not s[i].isalnum():
                i+=1
                continue

            if not s[j].isalnum():
                j-=1
                continue

            if (s[i].lower()==s[j].lower()):
                i+=1
                j-=1
            else:
                return False

2. 

给定一个字符串 s,将字符串中每个单词的字符顺序进行反转,同时仍保留空格和单词的初始顺序。

lass Solution:
    def reverseWords(self, s: str) -> str:
        i=0
        r=[]
        while i <len(s):
            j=i
            print(j)
            while s[j]!=' ':
                j+=1
                if j == len(s):
                    break
                
            for k in range(i,j):
                r.append(s[i+j-k-1])

            if j != len(s):
                r.append(" ")

            i=j+1
        
        return ''.join(r)

二、查找算法——字符串匹配问题

1. 单模式串匹配:从文本串T找出特定模式串 p 的所有出现位置。

        1)基于前缀搜索方法:从前往后遍历文本串T,在搜索窗口内从前向后逐个读入字符,找窗口中文本和模式串的最长公共前缀:KMP算法、 Shift-Or 算法

Brute Force 算法:BF 算法、暴力匹配算法、朴素匹配算法。逐个比较字符,每次向后移动一位。

最坏情况:总的比较次数为 m * (n - m + 1) 。时间复杂度为 O(m∗n)。

def BF(T: str, p: str) -> int:
    n, m =len(T), len(p)

    i, j= 0, 0 # 双指针,分别表示在文本串中的位置和在模式串中的位置

    while i < n and j < n:
        if T[i] == p[j]:
            i+=1
            j+=1

        else:
            i=i-j+1
            j=0

    if j == m:
        return i-j

    else:
        return -1

 KMP 算法:主串的某个子串等于模式串的某个前缀。若该前缀的前k个字符恰好等于后k个字符,则不用将i回退到搜索窗口第一个字符的位置,而是让它直接对准该模式串前缀的第k+1个字符,继续进行比对。

  • 用next数组来确定k:next[j] 表示的含义是:记录下标 j 之前(包括 j)的模式串 p 中,最长相等前后缀的长度。ABCAB的next[3]=1, next[4]=2。
  • 在构造前缀表阶段的时间复杂度为 O(m);在匹配阶段的时间复杂度是O(n),

 next数组生成(真的挺难懂的,特别是left=next[left-1])

def generateNext(p:str):
    m=len(p)
    next=[0 for _ in range(m)]

    left=0
    for right in range(1,m):
        
        while left>0 and p[left]!=p[right]:
            left=next[left-1]
        if p[left]==p[right]:
            left+=1
            next[right]=left

    return next

 KMP查找:

def kmp(s:str, p:str):
    n,m =len(s), len(p)
    
    next=generateNext(p)

    j=0
    for i in range(n):
        # 这里一定要用while不能用if,因为要不断前推直至为0或相等
        while j >0 and s[i]!=p[j]:
            j=next[j-1]

        if s[i]==p[j]:
            j+=1

        if j==m:
            return i-m+1


    return -1

        2)基于后缀搜索方法:同样从前往后遍历文本串T,但在搜索窗口内从后向前逐个读入字符,找窗口中文本和模式串的最长公共后缀。使用这种搜索算法可以跳过一些文本字符,从而具有亚线性的平均时间复杂度。eg:

         3)基于子串搜索方法:同样从前往后遍历文本串T,但在搜索窗口内从后向前逐个读入字符,找窗口中满足「既是窗口中文本的后缀,也是模式串的子串」的最长字符串。同样具有亚线性的平均时间复杂度:Rabin-Karp 算法使用了基于散列的子串搜索算法。

Rabin Karp 算法:比较T 所有长度为m的子串的哈希值与p是否相等,若相等再逐位比较(避免哈希冲突的出现)。优点还在于可以根据上一个子串的哈希值,快速计算相邻子串的哈希值,从而使得每次计算子串哈希值的时间复杂度降为了 O(1)——滚动哈希算法:

  • 假设给定的字符串的字符集中只包含 d 种字符,那么我们就可以用一个 d 进制数表示子串的哈希值。
  • 则右移一位的新子串的哈希值为
  • 为了避免哈希值过大溢出,最后还需要对其取一个较大质数q的模,这样可以尽量减少哈希冲突。
def rk(T: str, p: str, d, q):
    m = len(p)
    n = len(T)

    if n < m :
        return -1

    # 计算模式串和文本串第一个子串的哈希值
    hash_p, hash_sub_T = 0, 0
    for j in range(m):
        hash_p=( ord(p[j])+hash_p*d ) % q
        hash_sub_T=( ord(T[j])+hash_sub_T *d ) % q
    
    i, k=0, 0

    Do:
        if hash_p == hash_sub_T:
            match = True
            for j in range(m):
                if T[i+j] != p[j]:
                    match = False
                    break
                
                if match:
                    return i
        
        i+=1
        hash_sub_T = (hash_sub_T - power * ord(T[i-1])) % q   # 移除字符 T[i-1]
        hash_sub_T = (hash_sub_T * d + ord(T[i - 1 + m])) % q   # 增加字符 T[i - 1 + m]
        hash_sub_T = (hash_sub_T + q) % q          # 确保 hash_sub_T >= 0
        
    Loop While i+k < n 
    
    return -1

2. 多模式串匹配问题:从文本串T找出一组模式串P= p1,p2,... 的所有出现位置。

查找算法代码实现

1. 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

KMP做法:

我的做法: 

class Solution:
    def repeatedSubstringPattern(self, str: str) -> bool:
        l=len(str)

        for s in range(2, l+1):
            if l % s ==0:
                p=l//s
                sub=str[0:p]

                for k in range(1,s):
                    pp=p*k
                    match=True

                    for i in range(p):
                        sub_match= True
                        if str[pp+i]!=sub[i]:
                            sub_match=False
                            break

                    if sub_match is False:
                        match=False
                        break

                if match:
                    return True

        return False

 (未完待续)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值