leedcode 字符串 链接
反转字符转 题目说明 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
题目解析 懒得解析了。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public : void reverseString (vector<char > &s) { int i = 0 ; int j = s.size () - 1 ; while (i < j) { swap (s[i++], s[j--]); } } };
整数反转 题目说明 给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
题目解析 将整数的每一位存在一个vector中,倒序再存入一个数字中。或者就不用这样麻烦,直接从最后一位拿出来放在前面就行。
因为只能使用32位整数,所以需要使用下面的方法进行判断是否超界。
我们需要在「推入」数字之前,判断是否满足
INT_MIN=<ans*10+i<=INT_MAX
INT_MAX=2^31-1=2147483647=[INT_MAX]/10*10+7 所以可以变为ans*10+i<=[INT_MAX]/10*10+7 所以:[ans-[INT_MAX]/10]*10<=7-i 当
ans>[INT_MAX]/10 左边大于10,绝对大于右边。所以不行。
ans=[INT_MAX]/10 由于原数字符合条件,所以其最高位=1或者2,即7-i>0 左边为零,满足。
ans<[INT_MAX]/10 显然满足
综上只需判断ans<=[INT_MAX]/10**即可 对于负数同理存在 **ans>=[INT_MIN]/10 所以判别条件可变为[INT_MIN]<=ans<=[INT_MAX]/10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public : int reverse (int x) { vector<int > v; int a = x; while (a != 0 ) { v.push_back (a % 10 ); a /= 10 ; } long ans = 0 ; for (int i : v) { if (ans < INT32_MIN / 10 || ans > INT32_MAX) return 0 ; ans *= 10 ; ans += i; } if (ans > INT32_MAX) return 0 ; return ans; } };
字符串中的第一个唯一字符 题目说明 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
题目解析 方法一 依次存入unordered_multimap<char,int> 第一个是字符,第二个是索引.遍历字符串,设当前字符为,c,则count(c)==1?,相等则返回直至最后没有相等的返回-1;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public : int firstUniqChar (string s) { unordered_multimap<char , int > map; for (int i=0 ;i<=s.size ()-1 ;i++) { map.insert ({s[i],i}); } for (int i = 0 ; i <= s.size ()-1 ; i++) { if (map.count (s[i]) == 1 ) return i; } return -1 ; } };
题目解析 方法二 或者放入unordered_map<char,int> 第一个是字符,第二个是该字符出现的次数。全部插入后,遍历string,寻找map[s[i]]==1;并返回i;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public : int firstUniqChar (string s) { unordered_map<char , int > map; unordered_map<char , int >::iterator it; for (int i = 0 ; i <= s.size () - 1 ; i++) { map[s[i]]++; } int min = -1 ; for (int i = 0 ; i <= s.size (); i++) { if (map[s[i]] == 1 ) return i; } return min; } };
题目解析 方法三 依次存入unordered_map<char,int> 第一个是字符,第二个是索引.遍历字符串插入,若存在则索引变为-1。遍历map,寻找索引不为-1的最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { public : int firstUniqChar (string s) { unordered_map<char , int > map; unordered_map<char , int >::iterator it; for (int i = 0 ; i <= s.size () - 1 ; i++) { it = map.find (s[i]); if (it == map.end ()) { map.insert ({s[i], i}); } else { map[s[i]] = -1 ; } } int min = s.size (); for (it = map.begin (); it != map.end (); it++) { if (it->second != -1 ) { if (it->second < min) min = it->second; } } if (min == s.size ()) return -1 ; return min; } };
题目解析 方法四 unordered_map<char,int> map; //字符和索引值 deque<pair<char,int>> dq; //字符和索引值
使用一个额外的队列来查找最前面的为一个那个字符。遍历字符串,若当前字符再map中未出现的话,就插入map并且从后面插入队列。反之,则令该key在map中的值为-1,将队列中从头开始的所有字符在map对应的value==-1的元素全部弹出。则遍历完字符串之后剩下的队列的front就是所求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public : int firstUniqChar (string s) { unordered_map<char , int > map; deque<pair<char , int >> dq; for (int i = 0 ; i <= s.size () - 1 ; i++) { if (map.count (s[i]) == 0 ) { map[s[i]] = i; dq.push_back ({s[i], i}); } else { map[s[i]] = -1 ; while (!dq.empty () && map[dq.front ().first] == -1 ) dq.pop_front (); } } return dq.empty () ? -1 : dq.front ().second; } };
有效的字母异位词 题目说明 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。(两个字符串排序后相等)
题目解析 方法一 先判断两个字符串的长度是否相等,若不想等则返回false,使用两个哈希表<char,int>存储字符和出现的次数,在遍历一个哈希表,查找其对应的字符在两个哈希表中出现的次数是否一样?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public : bool isAnagram (string s, string t) { if (s.size () != t.size ()) return false ; unordered_map<char , int > map1; unordered_map<char , int > map2; unordered_map<char , int >::iterator it; for (int i = 0 ; i <= s.size () - 1 ; i++) { map1[s[i]]++; map2[t[i]]++; } for (it = map1.begin (); it != map1.end (); it++) { if (it->second != map2[it->first]) return false ; } return true ; } };
但是这么简单的问题使用哈希表就有些小题大做了,直接使用一个数组 vector v(26,0),即可,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public : bool isAnagram (string s, string t) { if (s.size () != t.size ()) return false ; vector<int > v (26 , 0 ) ; for (char c : s) v[c - 'a' ]++; for (char c : t) if (--v[c - 'a' ] < 0 ) return false ; return true ; } };
题目解析 方法二 对两个字符串排序之后判断是否相等即可
1 2 3 4 5 6 7 8 9 10 11 12 class Solution { public : bool isAnagram (string s, string t) { if (s.size () != t.size ()) return false ; sort (s.begin (), s.end ()); sort (t.begin (), t.end ()); return (s == t); } };
验证回文串 题目说明 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
题目解析 先将串中的无关字符舍去并处理相关字符,再使用双指针从前后遍历判断即可。
直接使用双指针在遍历的时候处理也行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public : bool isPalindrome (string s) { if (s.empty ()) return true ; vector<char > v; for (char c : s) { if (c <= 'Z' && c >= 'A' ) v.push_back (c + 32 ); else if (c <= 'z' && c >= 'a' ) v.push_back (c); else if (c <= '9' && c >= '0' ) v.push_back (c); } int i = 0 ; int j = v.size () - 1 ; while (i < j) if (v[i++] != v[j--]) return false ; return true ; } };
字符串转换整数 (atoi) 题目说明 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。 将前面步骤读入的这些数字转换为整数(即,”123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。 如果整数数超过 32 位有符号整数范围 [−2^31, 2^31 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31 − 1 的整数应该被固定为 2^31 − 1 。 返回整数作为最终结果。 注意:
本题中的空白字符只包括空格字符 ‘ ‘ 。 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
题目解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 class Solution { public : int myAtoi (string s) { bool flag = 0 ; bool finish_0 = 0 ; long ans = 0 ; int a = -1 ; while (s[++a] == ' ' ) ; if (s[a] == '-' ) { flag = 1 ; a++; } else if (s[a] == '+' ) { flag = 0 ; a++; } int b = a; for (b = a; b <= s.size () - 1 ; b++) { char c = s[b]; if (c <= '9' && c >= '0' ) continue ; else break ; } b--; cout << a << b << endl; for (int i = a; i <= b; i++) { int digit = s[i] - '0' ; ans = ans * 10 + digit; cout << digit << " " ; if (ans > INT32_MAX) { ans = INT32_MAX; if (flag == 1 ) { ans++; } } } cout << endl; if (flag == 1 ) { ans = -ans; } if (ans > INT32_MAX) { ans = INT32_MAX; } if (ans < INT32_MIN) { ans = INT32_MIN; } cout << ans << endl; return ans; } };
实现 strStr() 题目说明 实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
题目解析 方法一 暴力判断即可,不过还是要节省一点时间的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Solution { public : int strStr (string haystack, string needle) { int m = haystack.size (); int n = needle.size (); if (m == 0 ) { if (n == 0 ) return 0 ; else return -1 ; } if (n == 0 ) return 0 ; int i = 0 ; int j = 0 ; while (1 ) { while (1 ) { cout << i << endl; if (m - i < n) return -1 ; if (haystack[i] != needle[0 ]) { i++; continue ; } else if (haystack[i + n - 1 ] != needle[n - 1 ]) { i++; continue ; } else break ; } if (m - i < n) return -1 ; int a = i; int b = 0 ; while (1 ) { if (b == n) return i; if (haystack[a++] != needle[b++]) break ; } i++; } } };
题目解析 方法二 KMP
kmp算法的简单介绍: 两个字符串s1,s2,指针t1,t2分别指向两个字符串中的字符。现在要从s1中寻找s2出现的最早的一次,没有出现的话就返回-1.
普通暴力算法是一个字符一个字符的去匹配,匹配失败了就从头开始并后移一位。 kmp的思想是可不可以在匹配失败之后向后多移动几位呢? 假设在第n位匹配失败,即s1[n]!=s2[n]则t2前移几位,前移的程度要最大,即移动过后t2之前的字符仍然要保持匹配,再比较t2当前和t1是否对应字符相等即可,重复t2==0仍然匹配失败的话说明此处t1指向的字符是不可能正确的,必须略过,所以t1++,之后又开始了新一轮的比较。知道最后匹配成功则返回 t1-s2.size(),失败的条件不在赘述。 前面提到了需要再字符匹配失败之后t2向前移动,但是需要移动几位合适呢?现在t2前面a个字符都是匹配成功的,现在看这a个字符组成字符串a,如果a存在两个真子串 相等的话(设为a1,a2),显然a2已经是匹配成功的了,如果此时将a1移动到a2所在的地方那么自然也是匹配成功的了,所以上面提到的将t2前移几位的程度也呼之欲出,即t2要指向子串a1紧紧后面的那一位,这样就最大程度的利用了已知的信息。而这里提到的a1,a2就是a的最长相等真前缀和真后缀,所以问题的重点有落在了怎样求出s2的最长真前缀的长度呢??? 我们用数组 kmp<int> 来表示s2每一位对应的最长真前缀的长度。怎么求呢? 明显kmp[0]=0,因为只有一个字符并没有真前缀和真后缀。我们这里使用两个指针now和x分别指向a1,a2要进行匹配的字符。 如果s2[x]==s2[now]则显然可以匹配的字符数量要加1,即kmp[x]==kmp[x-1]+1; 如果不相等的话,就需要查看前面的就要像上面s2和s1进行你匹配是那样指针前移,这里又要移动几位呢?和上面的分析一样,显然kmp[now-1]个字符已经匹配成功,所以需要将**now=kmp[now-1]**在进行s2[x]==s2[now]是否相等的比较了,不成立的话再进行now=kmp[now-1];若now==0,则判断s2[0]==s2[x],不等的话说明连开头和结尾的字符都不想等,则显然kmp[x]=0;好了,算出了kmp[x],x++,进行下一论循环即可。 哈哈哈,终于写明白了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 class Solution { public : int strStr (string haystack, string needle) { if (needle.empty ()) return 0 ; if (haystack.empty ()) return -1 ; string s1 (haystack) ; string s2 (needle) ; cout << s1 << endl << s2 << endl; vector<int > kmp (s2.size(), 0 ) ; kmp[0 ] = 0 ; for (int i = 1 ; i <= s2.size () - 1 ; i++) { if (s2[kmp[i - 1 ]] == s2[i]) { kmp[i] = kmp[i - 1 ] + 1 ; } else { int x = i; int now = kmp[i - 1 ]; while (1 ) { cout << now << x << endl; if (s2[now] == s2[x]) break ; if (now >= 1 ) now = kmp[now - 1 ]; if (now <= 0 ) { now = -1 ; if (s2[0 ] == s2[x]) now = 0 ; break ; } } kmp[i] = now + 1 ; } } for (int x : kmp) cout << x << " " ; cout << endl; cout << "匹配" << endl; int t1 = 0 ; int t2 = 0 ; while (1 ) { cout << "t1: " << t1 << " t2: " << t2 << endl; if (t2 == s2.size ()) { return t1 - s2.size (); } if (t1 == s1.size () && t2 != s2.size ()) { return -1 ; } if (s1[t1] != s2[t2]) { if (t2 != 0 ) { t2 = kmp[t2 - 1 ]; } else { t1++; } } else { t1++; t2++; } } } };
题目解析 方法三 还有一个基于kmp算法的改进版。我们将两个字符串拼接起来为s,并使用一个两串中都不存在的字符’#’作为分隔符(如 s1=”aaa” s2=”aa” 的情况,如果中间没有#的话就不行,因为这种情况无论如何算kmp的时候都会算上两个子串的公共部分)。s=s2+’#’+s1; 别加反了。。。 遍历s,根据上面的算法求解s的kmp数组,若kmp[n]==s2.size()且n>=2*s2.size(),这样保证是s1的子串和s2匹配,而不是s2的或者s1和s2中间共有的。结果返回i - 2 * s2.size()即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class Solution { public : int strStr (string haystack, string needle) { if (needle.empty ()) return 0 ; if (haystack.empty ()) return -1 ; string s1 (haystack) ; string s2 (needle) ; string s = s2 + "#" + s1; cout << s1 << endl << s2 << endl << s << endl; vector<int > kmp (s.size(), 0 ) ; kmp[0 ] = 0 ; for (int i = 1 ; i <= s.size () - 1 ; i++) { if (s[kmp[i - 1 ]] == s[i]) { kmp[i] = kmp[i - 1 ] + 1 ; } else { int x = i; int now = kmp[i - 1 ]; while (1 ) { if (s[now] == s[x]) break ; if (now >= 1 ) now = kmp[now - 1 ]; if (now <= 0 ) { now = -1 ; if (s[0 ] == s[x]) now = 0 ; break ; } } kmp[i] = now + 1 ; } if (kmp[i] == s2.size ()) { if (i >= 2 * s2.size ()) { return (i - 2 * s2.size ()); } } } return -1 ; } };
外观数列 题目说明 给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1” countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
1
11
21
1211
111221
第一项是数字 1 描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11” 描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21” 描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211” 描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”
题目解析 很好理解题意,就是遍历并且判断数量即可。 使用一个数组vector<vector> v来保存结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution { public : string countAndSay (int n) { string s = "1" ; vector<vector<int >> v; vector<int > temp; v.push_back (temp); v[0 ].push_back (1 ); for (int i = 1 ; i <= n - 1 ; i++) { v.push_back (temp); int j = 0 ; while (j <= v[i - 1 ].size () - 1 ) { int count = 0 ; int num = v[i - 1 ][j]; int k = j; while (k <= v[i - 1 ].size () - 1 ) { if (v[i - 1 ][k] == num) { k++; count++; } else break ; } j = k; v[i].push_back (count); v[i].push_back (num); } s.clear (); for (int j = 0 ; j <= v[i].size () - 1 ; j++) { char c = v[i][j] + '0' ; s.push_back (c); } } return s; } };
最长公共前缀 题目说明 编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
题目解析 方法一 纵向扫描,若不符合条件则退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Solution { public : string longestCommonPrefix (vector<string> &strs) { string s = "" ; int min = INT32_MAX; int strnum = strs.size (); vector<string>::iterator it; if (strs.size () <= 1 ) return strs[0 ]; for (it = strs.begin (); it != strs.end (); it++) { min = (min < it->size ()) ? min : it->size (); cout << min << endl; } if (min == 0 ) return s; cout << "至少两个字符串,而且没有空串" << endl; int index = 0 ; int v = 0 ; for (index = 0 ; index <= min - 1 ; index++) { char c = strs[0 ][index]; int flag = 0 ; for (v = 0 ; v <= strnum - 1 ; v++) { if (strs[v][index] != c) { flag = 1 ; } } if (flag == 0 ) { s.push_back (c); } else { break ; } } return s; } };
题目解析 方法二 横向扫描,遍历字符串数组,得到相邻字符串的公共前缀,在用这个公共前缀与下一个字符串进行比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public : string longestCommonPrefix (vector<string> &strs) { string s = strs[0 ]; int strnum = strs.size (); for (int i = 1 ; i <= strnum - 1 ; i++) { s = CommonPrefix (s, strs[i]); cout << s << endl; } return s; } string CommonPrefix (string &s1, string &s2) { string s = "" ; int len = (s1.size () > s2.size ()) ? s2.size () : s1.size (); if (len == 0 ) return s; for (int i = 0 ; i <= len - 1 ; i++) { if (s1[i] == s2[i]) s.push_back (s1[i]); else break ; } return s; } };
题目解析 方法三 分治
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public : string longestCommonPrefix (vector<string> &strs) { string s = "" ; if (strs.size () == 0 ) return s; if (strs.size () == 1 ) return strs[0 ]; s = Prefix (strs, 0 , strs.size ()); return s; } string Prefix (vector<string> strs, int begin, int end) { int mid = (begin + end) / 2 ; if (end - begin == 1 ) return strs[begin]; else if (end - begin == 2 ) { return CommonPrefix (strs[begin], strs[begin + 1 ]); } else { return CommonPrefix (Prefix (strs, begin, mid), Prefix (strs, mid, end)); } } string CommonPrefix (string s1, string s2) { string s = "" ; int len = (s1.size () > s2.size ()) ? s2.size () : s1.size (); if (len == 0 ) return s; for (int i = 0 ; i <= len - 1 ; i++) { if (s1[i] == s2[i]) s.push_back (s1[i]); else break ; } return s; } };