LeetCode 005 字符串系列
Posted Al_tair
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 005 字符串系列相关的知识,希望对你有一定的参考价值。
字符串(三)
大家好!我是小笙!我前段时间忙于学习java后端技术,耽误了算法题的更新,心有惭愧,立下每周的刷题计划不攻自破,现在我再一次踏上刷题之路,坚持下去!加油!
字符串(三)系列题型如下
字符串系列三
子序列(392)
392.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T
的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = “abc”, t = “ahbgdc” 输出:true 示例 2:
输入:s = “axc”, t = “ahbgdc” 输出:false
提示:
0 <= s.length <= 100
0 <= t.length <= 10^4
两个字符串都只由小写字符组成。
方法一 下标法(MyCode)
我的想法是根据s的下标移动来解决该问题
首先呢,要想判断s是否是t 的子序列,我觉得肯定是要用到s字符串中的每个字符,所以根据下标移动,一旦满足全部字符按顺序在t中出现,那就返回true
注意 s,t为空的情况!!!
class Solution {
public boolean isSubsequence(String s, String t) {
int indexS = 0,n = s.length(), m = t.length();
if(n == 0) return true;
if(m == 0) return false;
for(int i=0;i<m;i++){
if(s.charAt(indexS) == t.charAt(i)){
indexS++;
}
if(indexS == n){
return true;
}
}
return false;
}
}
执行用时:1 ms, 在所有 Java 提交中击败了85.17%的用户
内存消耗:36.2 MB, 在所有 Java 提交中击败了77.57%的用户
方法二:动态规划(Other’s Code)
我觉得官方这题用动态规划有点麻烦!
首先让我讲述一下动态规划的概念
动态规划其实质上是通过开辟记录表,记录已求解过的结果,当再次需要求解的时候,可以直接到
那个记录表中去查找,从而避免重复计算子问题来达到降低时间复杂度的效果。实际上是一个空间
换时间的算法。动态规划,通常可以把指数级的复杂度降低到多项式级别。
形象的描述:拿着字典去找字
思路:
1.先创建“字典”(根据t长度动态考虑该解)
2.拿着“字典”去判断是否有该“字”
3.判断: false / true
class Solution {
public boolean isSubsequence(String s, String t) {
int n = s.length(), m = t.length();
int[][] f = new int[m + 1][26];
for (int i = 0; i < 26; i++) {
f[m][i] = m;
}
for (int i = m - 1; i >= 0; i--) {
for (int j = 0; j < 26; j++) {
if (t.charAt(i) == j + 'a')
f[i][j] = i;
else
f[i][j] = f[i + 1][j];
}
}
int add = 0;
for (int i = 0; i < n; i++) {
if (f[add][s.charAt(i) - 'a'] == m) {
return false;
}
add = f[add][s.charAt(i) - 'a'] + 1;
}
return true;
}
}
高精度运算(66)
66.加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1: 输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。
示例 2: 输入:digits = [4,3,2,1] 输出:[4,3,2,2] 解释:输入数组表示数字 4321。
示例 3: 输入:digits = [0] 输出:[1]
提示: 1 <= digits.length <= 100 0 <= digits[i] <= 9
方法一 入栈和出栈(MyCode)
class Solution {
public int[] plusOne(int[] digits) {
int carry = 1;
Stack<Integer> stack = new Stack<Integer>();
for (int i = digits.length - 1; i >= 0 || carry != 0; i--) {
int sum = carry;
if (i >= 0) {
sum += digits[i];
}
stack.push(sum % 10);
carry = sum / 10;
}
int[] res = new int[stack.size()];
for (int i = 0; i < res.length; i++) {
res[i] = stack.pop();
}
return res;
}
}
执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:37 MB, 在所有 Java 提交中击败了20.84%的用户
方法二 绝!(Other’sCode)
该解法真的绝!好妙啊!已经不能让我用其他词语形容该解法了
class Solution {
public int[] plusOne(int[] digits) {
for (int i = digits.length - 1; i >= 0; i--) {
digits[i]++;
digits[i] = digits[i] % 10;
if (digits[i] != 0) return digits;
}
digits = new int[digits.length + 1]; // 经典
digits[0] = 1;
return digits;
}
}
字符串变换(6)
6. Z 字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = “PAYPALISHIRING”, numRows = 3
输出:“PAHNAPLSIIGYIR”
示例 2:
输入:s = “PAYPALISHIRING”, numRows = 4
输出:“PINALSIGYAHRPI”
解释:
P I N
A L S I G
Y A H R
P I
示例 3:
输入:s = “A”, numRows = 1 输出:“A”
提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、’,’ 和 ‘.’ 组成
1 <= numRows <= 1000
方法一 遍历(MyCode)
如下是我的解题思路:
class Solution {
public String convert(String s, int numRows) {
String[] string = new String[numRows];
Boolean flag = true; // true 向下 false 向上
int count = 0;
String sum = "";
for(int i=0;i<numRows;i++){
if(string[i]==null){
string[i]="";
}
}
if(numRows == 1){
return s;
}
for(int i=0;i<s.length();i++){
// 判断运行方向和行数
if(flag == true && count<=numRows-1){ // 判断向下运行numRows-1
count++;
}else if(flag == true && count>numRows-1){
count--;
flag = false;
}else if(flag == false && count>1){ //判断向上运行numRows-1
count--;
}else if(flag == false && count==1){
count++;
flag = true;
}
string[count-1] += s.charAt(i);
}
for(int i=0;i<numRows;i++){
sum += string[i];
}
return sum;
}
}
执行用时:14 ms, 在所有 Java 提交中击败了22.71%的用户
内存消耗:39.3 MB, 在所有 Java 提交中击败了15.16%的用户
字符串匹配(459)
459. 重复的子字符串
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: “abab”
输出: True
解释: 可由子字符串 “ab” 重复两次构成。
示例 2:
输入: “aba”
输出: False
示例 3:
输入: “abcabcabcabc”
输出: True
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)
方法一分段比较
解题思路:
首先我用depart用来判断分段次数,countflag用来判断是否每一个分段都是相同的
我想最多分 字符串长度n 的段数 while(depart<=n)
class Solution {
public boolean repeatedSubstringPattern(String s) {
int n = s.length();
int depart = 2; // 分几份
int countflag = 0; // 判断标志位
while(depart<=n){
if(n%depart == 0){
for(int i=0;i<=n-n*2/depart;i+=n/depart){
if(s.substring(i,i+n/depart).equals(s.substring(i+n/depart,i+n*2/depart))){
countflag++;
}
}
if(countflag == depart-1){
return true;
}
}
countflag = 0;
depart++;
}
return false;
}
}
执行用时:34 ms, 在所有 Java 提交中击败了45.58%的用户
内存消耗:38.6 MB, 在所有 Java 提交中击败了92.36%的用户
方法二:字符串匹配(Other’s Code)
绝!太强了!无话可说,透彻!
// int indexOf(int ch, int fromIndex)
// 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
class Solution {
public boolean repeatedSubstringPattern(String s) {
return (s + s).indexOf(s, 1) != s.length();
}
}
中心拓展法(5)
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a” 输出:“a” 示例 4:
输入:s = “ac” 输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
方法一暴力解法(MyCode)
首先我想的是写出一个函数判断回文函数palindrome
然后我尝试通过双重循环来从最长字符串的回文字符串开始判断
一旦出现判断是回文函数就返回该字符,反之继续查询
最后还是无解就返回该字符串首字符s.substring(0,1)
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
for(int i=n;i>=2;i--){
for(int j=0;j+i<=n;j+=1){
if(palindrome(s.substring(j,j+i))){
return s.substring(j,j+i);
}
}
}
return s.substring(0,1);
}
public boolean palindrome(String s){ // 判断回文函数
int i=0,j=s.length()-1;
while(i<j){
if(s.charAt(i) == s.charAt(j)){
i++;
j--;
}else{
return false;
}
}
return true;
}
}
方法二:动态规划
动态规划的边界条件:
对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串}“bab” 是回文串,那么“ababa” 一定是回文串,这是因为它的首尾两个字母都是 “a”。
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P(i,j)P(i,j) 表示字符串 ss 的第 ii 到 jj 个字母组成的串(下文表示成 s[i:j]s[i:j])是否为回文串:
public class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
boolean[][] dp = new boolean[len][len];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= len; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= len) {
break;
}
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
以上是关于LeetCode 005 字符串系列的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode-Algorithms #005 Longest Palindromic Substring, Database #179 Consecutive Numbers