三、字符串类
2020.9.13 小杨缓慢但前进
-
重复的子字符串:
这个题看起来简单,实际不知道如何下手。题解给了三种算法,一种是验证周期性,一种是用s+s掐头去尾之后再看有没有s,一种是采用KMP算法。方法一就是对前一半的字符进行遍历,只要碰到能被字符串长度整除的序号i,就从i开始一直到最后的字符进行验证,看s[j]是否等于s[j-i],验证这个周期性。法二已经描述过,注意一下变成技巧就是一句话:(s+s).find(s,1)!=len(s),这句话的意思就是从1开始找(去掉开头),并且不希望返回值是从n开始的,也就是不是后面的s,注意find函数返回的是匹配到的B字符串的开头在A中的位置。法三就是KMP了,KMP是在A字符串中去匹配B字符串的算法。
著名的KMP算法就是两步,第一步,创建B字符串的所有前缀串对应的最长公共子串,第二步,根据前缀串去匹配,直到不匹配,当前不匹配的位置对应的字符的最长公共子串的长度对应的位置就是应该移到这个不匹配的位置的字符。
-
1370.上升下降字符串
没想到这个题也可以用桶排序做。反正涉及字母的正序和反序排列的都可以想到用这个。编程里面用到很多小技巧,一是使用ord函数转化为ascii码,再减去97就是0-25了。需要转回去就用chr()。另外就是在for循环中还可以用到any/all这种,比如,any(h[i]>0 for i in range(26))。
-
01.06 字符串压缩
俺做的和答案一样。
-
468. 验证IP地址
除了土方法判断之外,还有IpAddress模块可以用(结合try catch结构使用,可以直接拿来但不灵活),移植性最强的还是正则表达式这个东西。
可以先写一个ipv4和ipv6的chunk通用表达形式,再用+连接起来用原始字符串写出来。
ps:^和$表示匹配的是开头和结尾。r'^('+chunk_ipv4+r'\.){3}'+chunk_ipv4+r'$'【如果觉得这个式子复杂的话就看这个简化版:^(chunk_ipv4.){3}chunk_ipv4$,表达的就是这个意思,只不过加上了原始字符串和+连接之后就看起来乱了,去掉就好看了。分成了两部分,一个是从开头开始匹配的3个chunk加上点,一个是一个chunk一直到尾巴。至于为什么不能一次性从开头匹配4个chunk,是因为ipv4的结构就是chunk1.chunk2.chunk3.chunk4,最后结尾的后面没有点,只能单独拿出来】
其中chunk_ipv4=r'([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'【按照位数去讨论一个chunk的组成,分了一位/两位/三位去讨论,根据个位和十位的不同又继续分情况】
IPV6完整式子:r'^('+chunk_ipv6+r'\:){7}'+chunk_ipv6+r'$'其中chunk_ipv6=r'([0-9a-fA-F]){1,4}'
还没完。
这个通用表达式出来之后,用re.compile一下生成正则表达式对象之后再去匹配。
ipv6=r'^('+chunk_ipv6+r'\:){7}'+chunk_ipv6+r'$'
ipv6pattern=re.compile(ipv6)
对待检测表达式IP进行匹配:ipv6pattern.match(IP)即可 如果有匹配 再用group()就能取出来了
-
面试题 16.26 计算器
用我听的陈老师的算法课上讲的方法。利用双指针提取tokens——土方法生成全括号表达式——利用堆栈生成后缀表达式——利用堆栈计算后缀表达式
整了两天。。。
-
1249.移除无效的括号
陈老师的方法针不戳!用堆栈装左括号,遇到右括号就弹出!保证了一一对应的关系。最后有一个针对这个题目的小技巧(自己想的,暗中狂喜),用堆栈可以避免出现右括号多于左括号的情况(遍历到右括号就弹左括号,弹不出来的时候就不要输出这个右括号),但是没法避免左括号多出来的情况,因此多出来的左括号需要单独移除。这时候我发现多出来的左括号都是在后面的,因此应该先把字符串倒过来用replace消灭多出来的左括号,输出的时候再倒回去就好了。
-
4.寻找两个正序数组的中位数
难以置信这道题的级别是困难。我飘了。不过细想一下是python语言本身太优越了,很多功能正中下怀。也许对于其他语言来说,这道题需要颇费一番功夫。对八起,人家这个要分析时间复杂度和空间复杂度。可是我不想分析()不会。符合log(m+n)复杂度的算法是最小k值找寻。就是用二分法去确定排序为k的数,其中k为中位数顺序。每次都能排除掉k/2的数。
1328.破坏回文串
这道题没做出来,没有意识到破坏回文串和生成字典序最小的方法套路,实际上就是让所有不是a的都从a开始,如果已经全部都是a了,也就是aaaa这种了,那就把最后一个a改成b就行了。(字典序里面最接近aaaa的一个排列)。
面试题 17.13. 恢复空格
又是不会做的一道题,很遗憾,对递归没有深刻理解。有一点点思路就是,每增加一个,要么等于前面一个的未识别字符数加上当前没有被识别的这个字符数,也就是加1,要么就是新增的这个字符能和前面若干个字符连成一个字典里面的单词,那么就遍历从前面到当前字符为止的所有可能的情况,如果sentence[j:i](左闭右开)是某个单词,那么就可以有f[i]=f[j],至于能不能更新就还要比较一下这俩,取最小的就可以,依次遍历完之后就保证了取到了当前情况下(可能匹配到多个单词的)的最小值。
最后返回f[n]即可,表示的是长度为n的字符串中含有的最少的未识别字符。
面试题 16.18. 模式匹配
rega, regb = ('\\1', '\\2') if pattern[0] == 'a' else ('\\2', '\\1')
p = pattern.replace('a', '(\\w*)', 1).replace('b', '(\\w*)', 1).replace('a', rega).replace('b', regb)
p = '^' + p + '$'
m = re.match(p, value)
这道题虽然没有做出来,但是还挺有启发的,就扩张了对正则表达式的认知。只要你能总结出来,那么正则表达式就能给你表达出来。哪怕是对前面的复用。再次感受正则表达式的魅力,却还是没能记住它的脸哈哈。^和$代表匹配模式的开头和结尾。
1170. 比较字符串最小字母出现频次
嘿嘿,就排序这玩意儿,永远不会忘记就。希望永远不忘的,越来越多。