问题描述
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
思路
用一个哈希表来存储每个字母出现的次数,s中如果出现则该字母数量+1,t中如果出现该字母数量-1。这样,如果哈希表中所有字母数量都为0时,返回true,否则返回false。
代码
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length()!=t.length())return false;
int[] hash = new int[26];
for (int i = 0; i < s.length(); i++) {
hash[s.charAt(i)-'a']++;
hash[t.charAt(i)-'a']--;
}
for (int i = 0; i < 26; i++) {
if(hash[i]!=0)return false;
}
return true;
}
}
问题描述
给定两个字符串 s 和 t ,判断它们是否是同构的。
如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
输入输出样例
示例 1:
输入:s = “egg”, t = “add”
输出:true
示例 2:
输入:s = “foo”, t = “bar”
输出:false
思路
记录两个字符创中每一个字母第一次出现(或最后一次出现的位置),然后比较它们对应的每个字符第一次出现的位置是否相同,如果完全相同则返回true,否则为false。
代码
class Solution {
public boolean isIsomorphic(String s, String t) {
int[] preIndexOfs = new int[256]; //字符串中的字符包含字母或者数字
int[] preIndexOft = new int[256];
for (int i = 0; i < s.length(); i++) {
preIndexOfs[s.charAt(i)] = i; //记录每一个字母最后一次出现的索引位置
preIndexOft[t.charAt(i)] = i;
}
for (int i = 0; i < s.length(); i++){
//看他们对应字母最后一次出现位置是否相同
if(preIndexOfs[s.charAt(i)]!=preIndexOft[t.charAt(i)])return false;
}
return true;
}
}
问题描述
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = “abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:
输入:s = “aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
思路
从每一个字符开始,按照奇数偶数两种可能向外扩张(两个指针指向相同的数,向外移动),每次向外扩张一次,回文字符串的数量加1。
代码
class Solution {
public int countSubstrings(String s) {
int count=0;
for(int i=0;i<s.length();i++){
count+=help(s,i,i); //奇数回文
count+=help(s,i,i+1); //偶数回文
}
return count;
}
public int help(String s,int l,int r){
int count = 0;
while (l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)){
count++;
l--;
r++;
}
return count;
}
}
问题描述
给定一个字符串 s,统计并返回具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是成组连续的。
重复出现(不同位置)的子串也要统计它们出现的次数。
输入输出样例
示例 1:
输入:s = “00110011”
输出:6
解释:6 个子串满足具有相同数量的连续 1 和 0 :“0011”、“01”、“1100”、“10”、“0011” 和 “01” 。
注意,一些重复出现的子串(不同位置)要统计它们出现的次数。
另外,“00110011” 不是有效的子串,因为所有的 0(还有 1 )没有组合在一起。
示例 2:
输入:s = “10101”
输出:4
解释:有 4 个子串:“10”、“01”、“10”、“01” ,具有相同数量的连续 1 和 0 。
思路
观察字符串可以发现该规律:上一组相同字符串个数pre,大于等于当前相同字符串的个数current,即可有一组满足要求的子串,统计满足条件的字串数量即可。
代码
class Solution {
public int countBinarySubstrings(String s) {
int count=0,current=1,pre=0;
for (int i = 1; i < s.length(); i++) {
if(s.charAt(i)==s.charAt(i-1)){
current++;
}else {
pre = current;//使用pre保存上一组相同字符的数量
current = 1;//重新将当前改组字符串的数量初始化为1
}
if(pre>=current)count++;
}
return count;
}
}
问题描述
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
输入输出样例
示例 1:
输入:s = “3+2*2”
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
思路
就是一个没有特殊运算和括号的简易计算器。可以简化将符号栈简化为一个变量用于存储上一个运算符。用一个数字栈存储数字。
如果字符是数字,就将这些数字转换为int类型的值num。
对上一个运算符进行判断:
如果是“+”,直接将num放入数字栈中;
如果是“-”,将-num放入栈中;
如果是“*”,将栈顶元素弹出stack.pop()*num放入栈中;
如果是“/”,将栈顶元素弹出stack.pop()/num放入栈中;
最后将数字栈中的内容相加即为最终的结果。
代码
class Solution {
public int calculate(String s) {
char preSign = '+'; //由于第一个正数字前面的正号一般都省略,可以初始化为+,后面的正数可以根据其前运算符直接放入栈中
int num=0; //保存连续的数字字符组成的数字
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char c=s.charAt(i);
if(c<='9' && c>='0'){
num=num*10+(c-'0');
}
if((c=='+'||c == '-'||c == '*'||c == '/' )|| i==s.length()-1){//连续数字结束之后,要对num进行相应处理,同时更新preSign
if(preSign == '+'){
stack.offerLast(num);
}
else if(preSign == '-'){
stack.offerLast(-num);
}
else if(preSign == '*'){
stack.offerLast(stack.pollLast()*num);
}else if(preSign == '/'){
stack.offerLast(stack.pollLast()/num);
}
preSign = c;
num=0;
}
}
int sum=0;
while (!stack.isEmpty()){
sum+=stack.pollLast();
}
return sum;
}
}
问题描述
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
输入输出样例
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
思路
暴力解法很容易想到,本题最重要的是考察KMP算法。
KMP算法具体的过程可以参考:https://www.zhihu.com/question/21923021/answer/281346746
大致思路如下:
使用一个i指针指向待搜索字符串的首字符,j指向模板字符串的首字符。
如果使用暴力解法,则每次i指针向后移动到不匹配的字符后都需要回到原来的位置(的下一个位置),j回到模板字符的首字符重新开始遍历匹配比较。
KMP算法的优化在于,i指针每次比较向后移动一位且之后不再向前移动,j指针每次回溯到next[j]的位置,再向后移动比较。
代码大致为:
public int strStr(String haystack, String needle) {
int i=0,j=0;
int m = haystack.length();
int n = needle.length();
int[] next = getNext(needle);
while (j<n && i<m){
if(j==-1 || haystack.charAt(i)==needle.charAt(j)){
i++; //i只向前移动
j++;
}else j=next[j]; //j回溯到next[j]的位置
}
if(j==needle.length()){//如果j到了模板的最后面,说明完全匹配上了
return i-j; //匹配的初始位置=i当前位置-模板的长度
}
return -1;//i走到头,j没有走到头,说明没找到合适的匹配
}
现在重点在于找到next数组。
next数组i值的意义在于:遍历到模板i字符时,i前面的字符串前缀集合与后缀集合的交集中最长元素的长度。例如abcabc的前缀集合与后缀集合的交集中最长元素的长度为3。
求解next的过程,相当于使用一个模板字串p1(i从1开始)匹配另外一个相同模板字符串p2(j从0开始)的过程。如果对应的字符相同,则更新next值,如果不相同,j需要回溯到next[j]的位置。
代码为:
public int[] getNext(String pString){
int m = pString.length();
int[] next = new int[m+1];
next[0]=-1; //为了方便编程,将next数组初始位置置为-1
int i=0,j=-1;//求next过程类似以自身为模板进行匹配,i指向待匹配字符串,j为模板字符串
while (i<m){
if(j==-1 || pString.charAt(i)==pString.charAt(j)){
i++;
j++;
next[i]=j; //如果匹配,更新当前字符对应的next值
}else {
j = next[j];//如果不匹配,j向前回溯(回溯到当前next中记录的位置)
}
}
return next;
}
代码
class Solution {
public int strStr(String haystack, String needle) {
int i=0,j=0;
int m = haystack.length();
int n = needle.length();
int[] next = getNext(needle);
while (j<n && i<m){
if(j==-1 || haystack.charAt(i)==needle.charAt(j)){
i++;
j++;
}else j=next[j];
}
if(j==needle.length()){
return i-j;
}
return -1;
}
//next数组中的i值是i前面的字符串的前缀集合与后缀集合的交集中最长元素的长度
public int[] getNext(String pString){
int m = pString.length();
int[] next = new int[m+1];
next[0]=-1; //为了方便编程,将next数组初始位置置为-1
int i=0,j=-1;//求next过程类似以自身为模板进行匹配,i指向待匹配字符串,j为模板字符串
while (i<m){
if(j==-1 || pString.charAt(i)==pString.charAt(j)){
i++;
j++;
next[i]=j; //如果匹配,更新当前字符对应的next值
}else {
j = next[j];//如果不匹配,j向前回溯(回溯到当前next中记录的位置)
}
}
return next;
}
}
问题描述
给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。
在构造过程中,请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。
输入输出样例
示例 1:
输入:s = “abccccdd”
输出:7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
示例 2:
输入:s = “a”
输入:1
思路
使用一个哈希表统计每个字符出现的次数,如果出现偶数次,它就可以直接增加回文子序列的长度,如果是奇数次,它可以贡献(count/2)*2的长度。最后要最长的子回文,有奇数个的字符可以放在中间从而使自序列长度再加1。
代码
class Solution {
public int longestPalindrome(String s) {
Map<Character,Integer> map = new HashMap<>();
for(char c:s.toCharArray()){
map.put(c,map.getOrDefault(c,0)+1);
}
Set<Character> sets = map.keySet();
int count=0; //保存回文一半的长度
int num=0; //用于判断有没有奇数个的字符
for(char set:sets){
count+=map.get(set)/2;
if(map.get(set)%2==1){
num = 1;
}
}
return count*2+num;
}
}
问题描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入输出样例
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
思路
首先使用一个Hashmap存放每个字符出现的位置。使用一个i指针和一个j指针,j指针一直往前移动,当遇到map中存在的字符时,统计长度(j-i),i指针移动到存在的字符位置的下一个位置并将哈希表中i指针之前的字符删除。
最后还要考虑如果没有出现重复元素,则j移动到了串尾,无重复的字符长度为j-i。
代码
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> position = new HashMap<>();
int i=0,j=0;
int longest = 0;
while (i<s.length() && j<s.length()){
if(position.containsKey(s.charAt(j))){
longest = Math.max(longest,j-i);
int k=position.get(s.charAt(j));//重复元素所在的位置k
int t=i; //记录i的位置
i = k+1; //i移动到重复元素的下一个位置
while (t<=k){ //从哈希表中删除从i原位置到k之间的元素
position.remove(s.charAt(t));
t++;
}
}
position.put(s.charAt(j),j);
j++;
}
longest = Math.max(longest,j-i);
return longest;
}
}
问题描述
给你一个字符串 s,找到 s 中最长的回文子串。
输入输出样例
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
思路
和前面的 647. Palindromic Substrings (Medium) 有些类似,分别从偶数和奇数情况遍历字符串,然后用两个指针向外扩张。最后统计这些回文字符串中最长的一个。
代码
class Solution {
public String longestPalindrome(String s) {
String result="";
for (int i = 0; i < s.length(); i++) {
String a = Palindrome(s,i,i);
String b = Palindrome(s,i,i+1);
if(a.length()>b.length()){
if(a.length()>result.length()){
result = a;
}
}else {
if(b.length()>result.length()){
result = b;
}
}
}
return result;
}
public String Palindrome(String s,int l,int r){
while (l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)){
l--;
r++;
}
return s.substring(l+1,r);
}
}