字符串(二)
大家好!我是小笙!我们即将开始探索的是字符串(二)系列的题型,刷题一开始刷起来感觉很难,但是一直刷题一直爽,我们可以从中享受到刷题的快乐!加油!坚持下去
字符串(二)系列题型如下
字符串系列二
字符的统计(387,242)
387题 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = “leetcode”
返回 0
s = “loveleetcode”
返回 2
提示:你可以假定该字符串只包含小写字母。
方法一:暴力解法(MyCode)
对于这道题,我的想法是直接按题目要求,进行嵌套循环,寻找是否有重复的字符,方法暴力但不高效
class Solution {
public int firstUniqChar(String s) {
int n = s.length() - 1;
Boolean flag = false; // 用来判断有无找到重复的字符
for(int i=0;i<=n;i++){
flag = false;
for(int j=n;j>=0;j--){
if(s.charAt(i) == s.charAt(j) && i != j){
flag = true;
break;
}
}
if(flag == false){
return i;
}
}
return -1;
}
}
执行用时:41 ms, 在所有 Java 提交中击败了12.88%的用户
内存消耗:38.5 MB, 在所有 Java 提交中击败了94.21%的用户
方法二:函数的运用(Other’sCode)
函数是精髓!
// int indexOf(String str, int fromIndex)
// 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
// int lastIndexOf(int ch)
// 返回指定字符在此字符串中最后一次出现处的索引。
class Solution {
public int firstUniqChar(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) {
return i;
}
}
return -1;
}
}
方法三:使用哈希表存储频数(Other’sCode)
官方解题思路
在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回它的索引,否则在遍历结束后返回 -1−1。
// put() 将键/值对添加到 hashMap 中
// getOrDefault() 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> frequency = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); ++i) {
// 使用哈希映射统计出字符串中每个字符出现的次数
char ch = s.charAt(i);
frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);
}
for (int i = 0; i < s.length(); ++i) {
if (frequency.get(s.charAt(i)) == 1) {
return i;
}
}
return -1;
}
}
242题 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram” 输出: true
示例 2:
输入: s = “rat”, t = “car” 输出: false
提示:
1 <= s.length, t.length <= 5 * 104 s 和 t 仅包含小写字母
方法一:使用哈希表存储频数(MyCode)
本题目我使用哈希表去记录每个字母出现的次数进而判断是否互为字母异位词。
我的思路:
1.首先判断两者长度是否相等可以直接返回false
2.其次合并字符串到t字符串中 方便第3步遍历判断是否为两倍关系
3.然后分别记录不同字符串的对应的value值
4.最后只要遍历s字符串,然后比较字符出现是否为两倍关系来判断true/false
// put() 将键/值对添加到 hashMap 中
// getOrDefault(key,默认值) 返回获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
t = s + t; // 合并字符串
Map<Character, Integer> mapT = new HashMap<Character, Integer>();
Map<Character, Integer> mapS = new HashMap<Character, Integer>();
for(int i=0;i<s.length();i++){ // 1
char ch = s.charAt(i);
mapS.put(ch, mapS.getOrDefault(ch, 0) + 1);
}
for(int i=0;i<t.length();i++){ // 2
char ch = t.charAt(i);
mapT.put(ch, mapT.getOrDefault(ch, 0) + 1);
}
for(int i=0;i<s.length();i++){ // 3
if(mapS.get(s.charAt(i))*2 != mapT.get(s.charAt(i))){
return false;
}
}
return true;
}
}
执行用时:20 ms, 在所有 Java 提交中击败了10.38%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了18.34%的用户
方法二:排序(Other’s Code)
t 是 ss 的异位词等价于「两个字符串排序后相等」。因此我们可以对字符串 ss 和 tt 分别排序,看排序后的字符串是否相等即可判断。此外,如果 ss 和 tt 的长度不同,tt 必然不是 ss 的异位词。
// toCharArray() 方法将字符串转换为字符数组。
// sort(Object[] a) 对指定对象数组根据其元素的自然顺序进行升序排列。
// equals(long[] a, long[] a2) 如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] str1 = s.toCharArray();
char[] str2 = t.toCharArray();
Arrays.sort(str1);
Arrays.sort(str2);
return Arrays.equals(str1, str2);
}
}
数字与字符串之间的转换(412,38,13)
412题 Fizz Buzz
写一个程序,输出从 1 到 n 数字的字符串表示。
1.如果 n 是3的倍数,输出“Fizz”;
2.如果 n 是5的倍数,输出“Buzz”;
3.如果 n 同时是3和5的倍数,输出 “FizzBuzz”。
示例: n = 15,
返回:
[ “1”,
“2”,
“Fizz”,
“4”,
“Buzz”,
“Fizz”,
“7”,
“8”,
“Fizz”,
“Buzz”,
“11”,
“Fizz”,
“13”,
“14”,
“FizzBuzz” ]
方法一 遍历添加(MyCode)
思路:
该题目思路解释遍历1-15,然后检查是否是3或5的倍数
最后依次添加即可
class Solution {
public List<String> fizzBuzz(int n) {
List<String> list = new ArrayList<>();
String str;
for(int i=1;i<=n;i++){
str = String.valueOf(i);
if(i % 3 == 0 && i % 5 == 0){
list.add("FizzBuzz");
continue;
}
else if(i % 3 == 0){
list.add("Fizz");
continue;
}
else if(i % 5 == 0){
list.add("Buzz");
continue;
}
list.add(str);
}
return list;
}
}
执行用时:1 ms, 在所有 Java 提交中击败了99.33%的用户
内存消耗:39.3 MB, 在所有 Java 提交中击败了93.63%的用户
38题 外观数列
给定一个正整数 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”
要 描述一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如
数字字符串 “3322251” 的描述如下图:
示例 1:
输入:n = 1
输出:“1” 解释:这是一个基本样例。
示例 2:
输入:xn = 4 输出:“1211”
解释: countAndSay(1) = “1” countAndSay(2) = 读 “1” = 一 个 1 = “11” countAndSay(3) = 读 “11” = 二 个 1 = “21” countAndSay(4) = 读"21" = 一 个 2 + 一 个 1 = “12” + “11” = “1211”
提示:
1 <= n <= 30
方法一 遍历(MyCode)
简要说明
外层循环n 是作为字符串循环的层数,m是作为内层循环遍历字符串中的每个字符
然后通过count记录相等字符的数量来记录下一个字符的数据
// String.valueOf() 将括号里的基本数据类型数据装换成String类型
class Solution {
public String countAndSay(int n) {
String temp = "",sumOut = "1";
int count = 1; // 用来记录相等字符的数量
int m = sumOut.length();
for(int i=0;i<n-1;i++){
for(int j=0;j<m;j++){
if(j+1<m){ // 判断是否存在下个字符
if(sumOut.charAt(j) == sumOut.charAt(j+1)){
count++;
}else{
temp = temp + String.valueOf(count)+String.valueOf(sumOut.charAt(j));
count = 1;
}
}else{
temp = temp + String.valueOf(count)+String.valueOf(sumOut.charAt(j));
}
}
count = 1;
sumOut = temp;
m = sumOut.length();
temp = "";
}
return sumOut;
}
}
执行用时:27 ms, 在所有 Java 提交中击败了6.88%的用户
内存消耗:38.4 MB, 在所有 Java 提交中击败了17.89%的用户
方法二 递归(Other’s Code)
简单分析别人的代码
i 作为相同字符的首下标,j作为相同字符的尾下标,用ij来记录相同字符的长度类似我的count
通过递归层层剖析!妙啊!大佬!
class Solution {
public String countAndSay(int n) {
if (n == 1) return "1";
else {
String lastStr = countAndSay(n - 1); // 1 2 1 1
StringBuilder ans = new StringBuilder();
int i = 0, j = 1, len = lastStr.length();
while (j < len) {
if (lastStr.charAt(i) != lastStr.charAt(j)) {
ans.append(j - i).append(lastStr.charAt(i));
i = j;
}
j++;
}
ans.append(j - i).append(lastStr.charAt(i));
return ans.toString();
}
}
}
13题 罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如,
罗马数字 2写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: “III”
输出: 3
示例 2:
输入: “IV”
输出: 4
示例 3:
输入: “IX”
输出: 9
示例 4:
输入: “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15 s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’) 题目数据保证
s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
方法一 暴力哈希表解法(MyCode)
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
class Solution {
public int romanToInt(String s) {
Map<String,Integer> roman= Map.of("I"+"",1,"V"+"",5,"X"+"",10,"L"+"",50,"C"+"",100,"D"+"",500,"M"+"",1000);
String[] s_dp=new String[s.length()];
for (int i = 0; i < s.length(); i++) {
s_dp[i]=s.charAt(i)+"";
}
int sum = 0;
for (int i = 0; i < s.length(); i++) {
if(i==s.length()-1){
sum=sum+roman.get(s_dp[i]);
}
else if(roman.get(s_dp[i])>=roman.get(s_dp[i+1])){
sum=sum+roman.get(s_dp[i]);
}
else{
switch(roman.get(s_dp[i])+roman.get(s_dp[i+1])){
case 6:
sum+=4;
i++;
break;
case 11:
sum+=9;
i++;
break;
case 60:
sum+=40;
i++;
break;
case 110:
sum+=90;
i++;
break;
case 600:
sum+=400;
i++;
break;
case 1100:
sum+=900;
i++;
break;
default:
System.out.println("你的输入有误!!");
}
}
}
return sum;
}
}
执行用时:13 ms, 在所有 Java 提交中击败了7.92%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了13.31%的用户
方法二:模拟(Other’s Code)
思路:
分两种情况
通常情况下,罗马数字中小的数字在大的数字的右边。若输入的字符串满足该情况,那么可以将每个字符视作一个单独的值,累加每个字符对应的数值即可。
若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反。
class Solution {
Map<Character, Integer> symbolValues = new HashMap<Character, Integer>() {{
put('I', 1);
put('V', 5);
put('X', 10);
put('L', 50);
put('C', 100);
put('D', 500);
put('M', 1000);
}};
public int romanToInt(String s) {
int ans = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
int value = symbolValues.get(s.charAt(i));
if (i < n - 1 && value < symbolValues.get(s.charAt(i + 1))) {
ans -= value;
} else {
ans += value;
}
}
return ans;
}
}