文章目录
字符串题解(leetcode,持续更新中)
leetcode 3 无重复字符的最长子串
题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
分析
这道题主要用到了滑动窗口的思想
max定义为 最大不重复子串的长度,
left是滑动窗口的左指针,i是滑动窗口的右指针
定义一个hashmap ,用于存储字符以及字符的位置
对字符串中所有字符进行遍历
判断当前字符i 是否在hashmap中
如果在,需要更新滑动窗口的左指针
left=Math.max(left,map.get(s.charAt(i))+1);
无论左指针是否更新,都要更新 s.charAt(i)的位置
将s.charAt(i),存入hashmap中
重新计算此时的max
遍历完成,返回max即可
时间复杂度O(n)
代码
class Solution{
public int lengthOfLongestSubstring(String s){
if(s.length()==0) return 0;
HashMap<Character,Integer> map = new HashMap<Character,Integer>();//存储字符以及字符的位置
int max=0;//用于记录最大不重复子串的长度
int left=0;//滑动窗口左指针
for(int i=0;i<s.length();i++){//遍历整个字符串
if(map.containsKey(s.charAt(i))){ //判断当前字符是否在map中
//如果字符已经存在了,则更新左下标,向右移动一个字符
left=Math.max(left,map.get(s.charAt(i))+1);
}
map.put(s.charAt(i),i);//如果不包含,则将字符添加到map(字符,字符在数组下标),如果包含也需要更新s.charAt(i)的位置为当前的i,所以无论left是否有更新,都需要更新s.charAt(i)的位置
max=Math.max(max,i-left+1);//此时不重复子串的长度为:i-left+1,与原来的max相比,取最大值。
}
return max;
}
}
leetcode 17 电话号码的字母组合
题目
给定一个仅包含数字 2-9的字符串,返回所有能表示的字母组合。答案可以按任意顺序返回
类似于9键键盘
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
分析
用二叉树来实现,转换为求二叉树的所有路径
代码
leetcode 20 有效的括号
题目
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
分析
用栈实现
代码
class Solution {
public boolean isValid(String s) {
if(s.isEmpty())
return true;//字符串为空
Stack<Character> stack=new Stack<Character>();;//字符串-栈
for(char c:s.toCharArray()){//遍历字符串中的元素
if(c=='(')
stack.push(')');//如果传入的是左括号,则同时也将与之对应的右括号入栈
else if(c=='{')
stack.push('}');//同上
else if(c=='[')
stack.push(']');//同上
else if(stack.empty()||c!=stack.pop())//栈顶元素与c不同,则说明字符串无效
return false;
}
return stack.isEmpty();//栈中为空,则说明字符串有效,反之无效
}
}
leetcode 22 括号生成
题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
有效括号组合需满足:左括号必须以正确的顺序闭合。
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
输入:n = 1
输出:["()"]
分析
递归
代码
class Solution{
List<String> res =new ArrayList<>();
public List<String> generateParenthesis(int n){
if(n<=0){
return res;//返回最终的结果
}
getParenthesis("",n,n);//递归开始
return res;
}
private void getParenthesis(String str,int left,int right){
if(left ==0 && right ==0){
res.add(str);
return;
}
if(left==right){
//剩余左右括号数相等,下一个只能用左括号
getParenthesis(str+"(",left-1,right);
}else if(left<right){
//剩余左括号小于右括号,下一个可以用左括号也可用右括号
if(left>0){
getParenthesis(str+"(",left-1,right);
}
getParenthesis(str+")",left,right-1);
}
}
}
leetcode 49 字母异位词分组
题目
分析
代码
leetcode 139 单词拆分
题目
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。
分析
动态规划
代码
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
boolean [] dp = new boolean[s.length()+1];
dp[0] = true;
for(int i=1;i<=s.length();i++){
for(String word:wordDict){
int sz=word.length();
if(i-sz>=0 && s.substring(i-sz,i).equals(word)){
dp[i]=dp[i]||dp[i-sz];
}
}
}
return dp[s.length()];
}
}
leetcode 208 实现trie(前缀树)
题目
rie,又称前缀树或字典树,是一棵有根树,其每个节点包含以下字段:
指向子节点的指针数组 \textit{children}children。对于本题而言,数组长度为 2626,即小写英文字母的数量。此时 \textit{children}[0]children[0] 对应小写字母 aa,\textit{children}[1]children[1] 对应小写字母 bb,…,\textit{children}[25]children[25] 对应小写字母 zz。
布尔字段 \textit{isEnd}isEnd,表示该节点是否为字符串的结尾。
代码
class Trie{
class TireNode{
private boolean isEnd;//变量
TireNode[] next;
public TireNode(){//方法
isEnd = false;
next =new TireNode[26];
}
}
private TireNode root;//根节点
public Trie(){
root = new TireNode();
}
//插入
public void insert(String word){
TireNode node =root;
for(char c:word.toCharArray()){
if(node.next[c-'a'] == null){
node.next[c-'a'] = new TireNode();
}
node=node.next[c-'a'];
}
node.isEnd = true;
}
//查找
public boolean search(String word){
TireNode node =root;
for(char c:word.toCharArray()){
node = node.next[c-'a'];
if(node==null){
return false;//没找到
}
}
return node.isEnd;
}
public boolean startsWith(String prefix) {
TireNode node = root;
for (char c : prefix.toCharArray()) {
node = node.next[c - 'a'];
if (node == null) {
return false;
}
}
return true;
}
}
leetcode 394 字符串解码
题目
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
分析
示例 1:
输入:s = "3[a]2[bc]"
输出:"aaabcbc"
示例 2:
输入:s = "3[a2[c]]"
输出:"accaccacc"
示例 3:
输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
示例 4:
输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
代码
leetcode 438 找到字符串中所有字母异位词
题目
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指字母相同,但排列不同的字符串。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
分析
滑动窗口法+双指针
代码
leetcode 647 回文子串
题目
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
分析
动态规划
boolean[][] dp=new Boolean[s.length()][s.length()];
s.charAt(i)==s.charAt(j)&&(dp[i][j]=dp[i+1][j-1]||i-j<=2);
代码
class Solution {
public int countSubstrings(String s) {
boolean[][] dp=new boolean[s.length()][s.length()];
int ans=0;
for(int j=0;j<s.length();j++){
for(int i=0;i<=j;i++){
if(s.charAt(i)==s.charAt(j)&&(j-i<2||dp[i+1][j-1])){
dp[i][j]=true;
ans++;
}
}
}
return ans;
}
}