String
Python 里的string是immutable的
尽量避免做slicing
Char Removal
删除student中的u和n
常见错误:
如果有多个元素连续出现,index不注意维护就会删不干净。
调用API的时候要注意时间复杂度 deleteCharAt, subString, string1+string2都
两个挡板,三个区域,同向而行。对于需要保留output相对顺序的题目,选择同向而行。
左挡板左侧,不包含左侧,是要留的结果 左右之间,是不在乎的区域 右挡板右侧,不包含右侧,是正在探索的需求
Case 1: a[j] should be kept: a[i]=a[j], i++, j++ Case 2: a[j] should not be kept: j++
j 快指针,右边是未访问的元素,左边是已经访问的元素;
i 慢指针,左边是要返回的字符;
快慢指针之间[i, j-1]是不返回的元素。
initialization:i=0, j=0
str[j]是否该被保留
a = list(str)
Case1: 保留str[j], a[i]=str[j], i+=1. j+=1;
Case2: 不保留str[j], j+=1
Termination Condition: j>=len(str)
Time O(n)
Space O(n)
Time O(n)
Space O(n)
相比之下第一种更好,因为第二种里的append不一定是一个O(1)的操作。在Python中可变长度的容器,增加元素时要开辟新的空间(一般是double size),只是amortized O(1)。
删除句中的多余空格
原始输入空格比较多,把开头多余的空格、结尾的空格去掉,中间的只留1个空格。
Case 1: if a[f]==' ': - case 1.1 s==0, don't copy - case 1.2 s!=0, if a[s-1]!=’ ‘,copy if a[s-1]==' '. don't copy
Case 2: if a[f]!=' ': copy
Post-processing: s>0 and a[s-1] ==' ', then s-=1
initialization:i=0, j=0
Case 1: a[j]==' ' and i==0, ignore
Case 2: a[j]!=' ' , keep
Case 3: a[j]==' ' , and i!=0, and a[i-1]!=' ', keep
Case 4: a[j]==' ' , and i!=0, and a[i-1]==' ', ignore
Termination: j=n
Post-processing: i>0 and a[i-1] ==' ', then i-=1
Time O(n)
Space O(n)
Char De-duplication
第一个字符,不论是否重复,都一定会被保留,所以initialization时,可以都从1开始。
Case 1: a[j]==a[i-1], ignore (j+=1)
Case 2: a[j]!=a[i-1], keep (a[i]=a[j], i+=1, j+=1)
变形题1 如果可以允许两个一样的,那需要修改的只有2行:line5的初始值2,2; 以及line7的slow-2
变形题2 消消乐 维护一个stack,和栈顶一样就pop,最后返回所有在stack里的元素
fast = 0
stack = []
Case 1: a[fast]!=stack.top(), stack.push(a[fast]), fast+=1
Case 2: a[fast]==stack.top(), keep moving fast until a[fast]!=stack.top(), stack.pop()
要非常清楚fast本身的物理含义,在case2里,如果我们最终是要保留一个重复元素,那么就不需要pop()
同理,也可以再定义一个slow pointer,借助双指针实现inplace的操作。
f fast: linear scan pointer (fast and after are not processed)
s slow: all letters to the left of s (not including s) are processed letters that should be kept.
Initialization: slow=fast=1
Case 1: a[f]!=a[s-1], then a[s]=a[f], s++, f++;
Case 2: a[f]==a[s-1], then repeatedly f++ until a[f]!=a[s-1], s-=1
Return a[0:s] (不包含s)
如果输入是一个list,那么空间复杂度就是O(1); 如果是个string,因为它immutable,先转换成string,空间复杂度是O(n)
Reverse a string
iterative 双指针,头尾,相向,互换
recursive 虚线框中间
reverse句子
相当于reverse两次,call reverse这个helper,第一次 reverse整个list,第二次reverse单词,j放在j-1,然后再call helper
reverse right-hand side by two positions
abcdef --> efabcd
step 1: reverse the whole word,
step 2: fe | dcba 分别反转
Substring
检查一个字符串是否是另一个字符串的子串,返回字符串开始的位置
Brute-force O(m*n)
这里注意for和else搭配的用法,这是为了避免引入一个flag变量记录状态。如果正常退出,也就是没有执行break,else会执行;如果执行break,else不执行。这样在内部的for正常退出时h的位置就是match的字符串在原串的起始位置。
虽然乍一看这是一个O(m*n)的复杂度,但是在早年word之类的Ctrl+F查找时都是这套brute force。这是因为对于英文文本来说,只要前面几个字符匹配了,大概率这就是一个词,实际的时间复杂度其实更接近于O(m+n)
Rabin-Karp: Avg Case O(m-n), worst case:O(m*n) 把字符串的比较转化成26进制数的比较
比如,16进制表示FBA=15*16^2+11*16^1+10*16^0
如果用26进制数来表示abcd,那么a*26^3+2*26^2+3*26^1+4*26^0;
取模: 加速计算、保证时间复杂度是常数,避免大数比较
Char Replacement
Example: student --> stuXXt, studendendent -->stuXXXXXXt
slow: all letters to the left of slow (not including slow) are the final results fast: current index; s1: 'den', s2: 'XX'
student s f
case 1: from s1.length > = s2. length 替换后变短
case 1: if f!='d', copy, f++, s++ case 2: if f points to 'den', copy s2, s+=s2.length fast+=s1.lenth
case 2: s1.length < s2. length 替换后变长
这个时候替换成XXXX
step 1: 过一遍string,数一下s1字符串出现了多少次(比如2次); step 2: enlarge the string 2*(s2.length-s1.length);
s t u d e n t d e n t _ _ s f
step 3: 把slow放在最后,f放在真正的string最后,slow的右边,不含slow都是最后结果,该保留的;fast是现在的index。 出来的条件是f和s都离开了整个string。
Shuffling
ABCD1234-->A1B2C3D4
ABCD1234--> AB12CD34-->A1B2C3D4
A1B2C3D4-->ABCD1234
merge sort, 左右两边都sort好了,所以谁小移谁
A1B2|C3D4 / \ AB12 CD34 ABCD1234
12 CD 先换成 DC21,再内部交换一次CD12
ABCDEFG1234567-->A1B2C3D4E5F6G7
要让chunk1和chunk3同size:ABCD1234EFG567 要为每一个步骤求mid
size = right-left-1 mid = left+size/2 lm = left+size/4 rm = left+size*3/4
ABCDE1234567-->A1B2C3D4E567
当后面的67没有,接着做
ABCDEFG12345-->A1B2C3D4E5FG
ABCDE12345FG 54321GF -->12345FG
Permutation with dups
if no dups, swap-swap
if with dups
如果还是swap-swap的思路,那只要是换过来一样的,在hashset看到了,就不换了。这个判断需要加在进入for循环之前;另外里面加一个if判断 如果在里面,continue;把该加的加了
Time: O(n!*n) Space: O(n^2)
Encoding/Decoding
aaaabcc-->a4b1c2 run-length encoding, requires in-place
注意2种case
slow: 左侧不包含都是结果 fast: 当前
看f后面的已经不是f指向的时候了,总结之前的。但是注意这又有可能会overwrite到后面的一个index,如果string越变越长、越变越短...
Step 1: from left to right: we only deal with the cases where the letter repeats >=2 times; the case that appeared only once are simply copied. In the meantime, we need to count how many times a letter only repeats once. eg. count=1 Step 2: enlarge the string by 'count', and from right to left: do similar steps.
Sliding window
longest substring that each char occurs at most k times
[L(slow), R(fast)]: each char occurs at most k times within this window HashMap: <char, count>
BDEFGADE
| |
<B,1>, <D, 1>, <E, 1>, <F, 1>, <G, 1>, <A, 1>, <D, 1>
global max =6
when to move fast index: at most k times within the table (no value exceeds k times)
when to move slow index: when there is a value > k, keep slow++ until there is no value > k
when to update the final result: when we do fast ++, as long as (fast-slow+1)>global_max
find all anagrams of a substring s2 in a long string s1
string s2 = aabc s1 = zzzzcdebcaabcyywwww
这里有重复字母,所以不能用XOR来做。因为a XOR a 的时候得到的是0,这个时候不知道是aa的0还是bb的零
Fixed-Size sliding window 一个指针都行 HashMap <char, count>
Step 1: initialize the hashmap, put s2 in hashmap Step 2: run sliding window on s1, 出现一个里面的count减一个,目标是都是0
Time = O(m*n)
how to optimize to O(m+n)
用type_of_char_to_match来替代 每当有一个字母后面的数字是零,意味着type_of_char_to_match可以减一了,不用每次都对着hashmap查,而是对着数字查了。
flip at most k times to convert from 0 to 1, find the longest subarray that contains all 1
同上面的at most k times, 只需要0 at most k times
corner case
a to i (convert string to integer)
Assumption: the string is a valid integral number
null, empty string return 0
leading space ' 123' return 123
invalid characters '123 1' return 123, '123a1' return 123 '+ 123' return 0
sign (+,-) '+123' return 123 '-123' return -123 '+-123' return 0
overflow an integer return Integer.MAX_VALUE
overflow a long return Integer.MAX_VALUE
Backus-Naur form (BNF)
SPC::=' ' NUM::='0'|'1'|'2'...|'9' INTEGER::=(SPC*)['+' | '-'](NUM+)(SPC*)
::= 规则 () 组合 [] 可有可无 0/1 | 任选其一 + 至少1个 * 至少0个
sum>integer.maxvalue+1 或者-sum<-integer.minvalue
Validate if a string is numeric
科学技术法
‘0~9’, ‘+、-’,‘E’, 'e', ' ', ‘.’
+-: 要么是一开始的符号,要么是e之后紧接的符号 不然就false,最多2个 . : 只能出现一次,而且是e的前面 e: 只能出现最多1次,e的前后必须有数字
REGEX
string pattern
Last updated