剑指offer整理(附python代码)——字符串

1.替换空格

问题描述

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

考察点:字符串匹配
思路1 创建新的字符串存最终结果(以下采用2)
  1. 1st顺序遍历字符串,确定除空格外字符串长度
  2. 2nd顺序遍历字符串,顺序存储,碰到字符串就直接存储,空格就存‘20%’
思路2 在原数组上进行扩充
  1. 顺序遍历字符串,确定空格个数,确定新字符串长度
  2. 逆序遍历字符串,逆序存储,碰到字符串就直接存储,空格就存‘20%’
class Solution11:
    def replaceSpace(self, s):
        # write code here
        r=""
        s=s.split(" ")
        n=len(s)
        #取除最后一个字符外的所有字符
        for i in s[:n-1]:
            print i
            r=r+i+"%20"
        #将最后一个字符加入修改后的字符串
        r+=s[n-1]
        return r

if __name__ == '__main__':
    s = Solution11()
    print s.replaceSpace("123 hello 234")
123
hello
123%20hello%20234

2.二进制中1的个数

问题描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

考察点:位运算
  • 有符号数,正(首位0)、负(首位1)
  • 二进制中的5中运算:与(&)、或(|)、异或(^,相异为1)、左移(<<)、右移(>>)
  • 左移(m<<n):将m左移n位,左侧的舍去,右侧补0。
  • 右移(m>>n):将m左移n位,右侧舍去,左侧补0/符号位(若为有符号数)。
  • 二进制补码:正数(不变)、负数(除符号位外,其余按位取反,然后再加1)
思路
  • 可能陷入死循环的解法:从n的2进制形式的最右边开始判断是不是1。(跟1做“与&”) 但是:若输入时负数会陷入死循环,因为负数右移时,在最高位补得是1,那么会有无数个1了。
  • 正解思想:将待比较的数‘1’左移(其实后来就不是1了),和n的每位进行位与,来判断1的个数。 可以避免原数右移,可能增加1的情况。
  • 最优解思想:把一个整数减去1,再和原整数做“与”,会把该整数最右边一个1变为0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
    举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。 这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0. 那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
# 正解
# private static int NumberOf1_low(int n) {
#     int count = 0;
#     int flag = 1;
#     while (flag != 0) {
#         if ((n & flag) != 0) {
#             count++;
#         }
#         flag = flag << 1;
#     }
#      return count;
#     }
class Solution2:
    # 最优解
    def NumberOf1(self, n):
        # write code here
        count = 0
        if n<0:
            # 把负号去掉,0xFFFFFFFF代表一个16进制数,转为2进制为:32个1.
            n=n&0xFFFFFFFF
        while n:
            n = n&(n-1)
            count+= 1
        return count

if __name__ == '__main__':
    s = Solution2()
    print s.NumberOf1(-54)
28

3.字符串的排列

问题描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

考察点:字符串的全排列
思路1
  1. 将字符串分为两部分:首字符+其后的所有字符;求其后字符的全排列
  2. 拿第一个字符与其后的字符逐个交换
class Solution:
    def Permutation(self,ss):
        if not ss:
            return []
        if len(ss)==1:
            # 返回列表形式的字符串
            return list(ss)
        allList = []
        charList = list(ss)
        charList.sort()  #按字典顺序进行排列,不去重
        for i in range(len(charList)):
            # 限制i>0 : 防止charList[i-1]下标越界
            if i>0 and charList[i]==charList[i-1]:
                continue # 忽略重复的字符
            #Permutation():乱序,接受array类型及int类型的输入。
            #shuffle():乱序,只接受array类型输入。
            temp = self.Permutation(''.join(charList[:i])+''.join(charList[i+1:]))
            for j in temp:
                allList.append(charList[i]+j)
        return allList
s = Solution()
s.Permutation('abc')
            
['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

4.第一个只出现一次的字符

问题描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1

考察点:字符串的统计
思路1:O(n)
  1. 用字典统计每个字符出现的次数(自己统计)
  2. 按顺序,以str中字符为key,判断字典中统计的出现次数是否为1,是则输出下标。
思路2: 更快
  1. 用‘内置方法’统计字符串中字符出现的次数count()
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        sdict = {}
        for i in range(len(s)):
            if sdict.has_key(s[i]):
                sdict[s[i]] += 1
            else:
                sdict[s[i]] = 1
        for i in range(len(s)):
            if (sdict[s[i]]==1):
                return i 
        return -1
s = Solution()
s.FirstNotRepeatingChar('')
-1
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        for k,i in enumerate(s):
            # 内置函数,直接统计出现次数
            if s.count(i)==1:
                return k
        return -1
s = Solution()
s.FirstNotRepeatingChar('google')
4

5.左旋转字符串

问题描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

考察点:知识迁移
思路1:切片-拼接

需要占内存,遍历两次字符串

  1. 将S切成s[n:],s[:n]
  2. 拼接两部分s[n:]+s[:n],并返回
思路2: 定义字符串反转函数(剑指,更快)

调用3次reverse方法,reverse实则为首尾元素交换,然后首依次往后,尾依次往前,直到“首=尾”。

reverse方法复杂度0(1),列表按下标查找。

  1. 定义字符串所有字符都反转的函数 reverse(eg:abc->cba)
  2. 将字符串按第n位分割为两部分,分别进行 reverse
  3. 将反转后的两部分拼接后,再整体进行reverse
总结
  1. 字符串S无法直接按位交换,即s[i]=s[j]不合法;需S=list(S),再s[i]=s[j]即可。
# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if len(s)<n:
            return ''
        if len(s)==n:
            return s
        return s[n:]+s[:n]
# -*- coding:utf-8 -*-
class Solution:
    def reverse(self,s):
        if not s:
            return ''
        length = len(s)
        if length<=0:
            return ''
        s = list(s) #必须要有,不然不能使用s[i]=s[j]
        start = 0
        end = length-1
        while start<end:
            s[start],s[end]= s[end],s[start]
            start += 1
            end -= 1
        return ''.join(s)
    def LeftRotateString(self, s, n):
        # write code here
        length = len(s)
        if length<=0:
            return ''
        if n>length:
            n = n%length
        first = ''
        second = ''
        for i in range(n):
            first += s[i]
        for i in range(n,length):
            second += s[i]
        first = self.reverse(first)
        second = self.reverse(second)
        return self.reverse(first+second)

6.翻转单词顺序列

问题描述

一一的翻转字符串中所有单词的顺序,如将“I am a student.”翻转为“student. a am I”

考察点:知识迁移
思路:翻转函数reverse
  1. “整体翻转”:先将字符串中所有字符进行
  2. “局部翻转”:将字符串按空格进行分隔,每个单词分别翻转
总结
  • 字符串翻转时,需要按空格进行分词时,需记录首尾位置,不能直接用split(’ ')进行分割
# 错误,不能用split(' ')分隔字符串,翻转再拼接。因为“ ”测试不通过。
class Solution:
    def reverse(self,s):
        if not s:
            return ''
        length = len(s)
        if length<=0:
            return ''
        s = list(s) #必须要有,不然不能使用s[i]=s[j]
        start = 0
        end = length-1
        while start<end:
            s[start],s[end]= s[end],s[start]
            start += 1
            end -= 1
        return ''.join(s)
    def ReverseSentence(self, s):
        # write code here
        length = len(s)
        if length <=0:
            return ''
        if s ==" ":
            return " "
        s = self.reverse(s)
        s = s.split(' ')
        res = ''
        for i in s:
            i = self.reverse(i)
            res = res+i+' '
        return res.strip()
s = Solution()
s.ReverseSentence(' ')
'student. a am I'
class Solution:
    def reverse(self,s,start,end):
        if not s:
            return ''
        length = len(s)
        if length<=0:
            return ''
        s = list(s) #必须要有,不然不能使用s[i]=s[j]
        while start<end:
            s[start],s[end]= s[end],s[start]
            start += 1
            end -= 1
        return ''.join(s)
    def ReverseSentence(self, s):
        # write code here
        length = len(s)
        if length <=0:
            return ''
        #整体翻转
        s = self.reverse(s,0,length-1)
        #局部翻转
        st= 0
        e = 0
        i = 0
        while i<length:
            # 忽略单词开头的空格
            while i<length and s[i] == ' ':
                i += 1
            #记录非空格的第一个字符的位置
            st = i 
            e = i
            #找到第一个单词的结尾下标
            while i<length and s[i]!=' ':
                i += 1
                e += 1
            s = self.reverse(s,st,e-1)
        return s
s = Solution()
s.ReverseSentence('I am a student. ')            
' student. a am I'

7.把字符串转为整数

问题描述
  • 输入一个字符串,包括数字字母符号,可以为空
  • 如果是合法的数值表达则返回该数字,否则返回0
考察点:综合
思路
  • 首先判断首位,如果不是数字,则首位上字符的值为0;如果不是+/-,则数字非法,返回0。
  • 依次对其余位上的数求值,若遇到非数字,则数字非法,返回0.
  • 求和,返回。
class Solution:
    def StrToInt(self, s):
        # write code here
        if len(s)==0:
            return 0
        else:
            # 若首位非数字时,a=0
            if s[0]>'9' or s[0]<'0':
                a=0
                if s[0]!='+' and s[0]!='-':
                    return 0
            else:
                # 首位位数字,计算首位的值
                a=int(s[0])*10**(len(s)-1)
            if len(s)>1:
                for i in range(1,len(s)):
                    if s[i]>='0' and s[i]<='9':
                        a=a+int(s[i])*10**(len(s)-1-i)
                    else:
                        # 发现除首位外的位置上含非数字,则数值不合法,返回0
                        return 0
        if s[0]=='+':
            return a
        if s[0]=='-':
            return -a
        return a
s=Solution()
print s.StrToInt('+123')
print s.StrToInt('a123')
123
0

8.正则表达式匹配

问题描述

请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

思路:
# -*- coding:utf-8 -*-
class Solution:
    # s, pattern都是字符串
    def match(self, s, pattern):
        # write code here
        if len(s)==0 and len(pattern)==0:
            return True
        if len(s)>0 and len(pattern)==0:
            return False
        # 当模式中的第二个字符是“*”时
        if len(pattern)>1 and pattern[1]=="*":
        #如果字符串第一个模式跟模式第一个字符匹配(相等或匹配到“.”),可以有3种匹配方式
            if len(s)>0 and (s[0]==pattern[0] or pattern[0]=='.'):
            #1.模式后移2字符,相当于x*被忽略
            #2.字符串后移1字符,模式后移2字符
            #3.字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位
                return self.match(s,pattern[2:]) or self.match(s[1:],pattern[2:]) or self.match(s[1:],pattern)
            else:
                return self.match(s,pattern[2:])
        if len(s)>0 and (s[0]==pattern[0] or pattern[0]=='.'):
            return self.match(s[1:],pattern[1:])
        return False

9.表示数值的字符串

问题描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

思路1:正则表达式
  1. 是否匹配:re.match(r" ", mstr)
  2. ^:字符串的开始位置
  3. 特殊字符的匹配,需要加转译字符‘\’:$,(,),*,+,-,[,?,\,^,{,|。
  4. ?:匹配子表达式0次/1次;*:匹配子表达式0次/多次,+:匹配子表达式1次/多次。
# -*- coding:utf-8 -*-
import re
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        return re.match(r"^[\+\-]?[0-9]*(\.[0-9]*)?([eE][\+\-]?[0-9]+)?$",s)
思路2:判断是否能转为float
  1. 能转为float的才为数字
# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        try:
            p = float(s)
            return True
        except:
            return False

10.字符流中第一个不重复的字符

问题描述
  • 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
  • 如果当前字符流没有存在出现一次的字符,返回#字符。
思路
  1. 方法1:遍历字符串,调用mstr.count(‘i’)查找mstr中‘i’出现的次数。
  2. 方法2:遍历字符串,用字典存储‘字符—次数’,之后再遍历字典,找到出现1次的字符。【较快】
  3. 方法3:遍历字符串,用列表mlist依次存储字符出现的次数。当字符已存在时,用mlist.index(‘i’)获取其下标后,令其值加1。【最快】
# 方法1
# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    def __init__(self):
        self.mstr = ""
    def FirstAppearingOnce(self):
        # write code here
        for i in self.mstr:
            if self.mstr.count(i)==1:
                return i
        return '#'
    def Insert(self, char):
        # write code here
        self.mstr += char
# 方法2
# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    def __init__(self):
        self.s=''
        self.dict1={}
    def FirstAppearingOnce(self):
        # write code here
        for i in self.s:
            if self.dict1[i]==1:
                return i
        return '#'
    def Insert(self, char):
        # write code here
        self.s=self.s+char
        if char in self.dict1:
            self.dict1[char]=self.dict1[char]+1
        else:
            self.dict1[char]=1
# 方法3
# -*- coding:utf-8 -*-
class Solution:
    chars = []
    counts = []
      
    def __init__(self):
        self.chars = []
        self.counts = []
      
    def FirstAppearingOnce(self):
        # write code here
        counter = 0
        while counter<len(self.counts):
            if self.counts[counter]==1:
                return self.chars[counter]
            counter += 1
        return "#"
          
    def Insert(self, char):
        # write code here
        if char in self.chars:
            self.counts[self.chars.index(char)] += 1
        else:
            self.chars.append(char)
            self.counts.append(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值