一、通过删除字母匹配到字符字典中的最大值
给你一个字符串 s
和一个字符串数组 dictionary
,找出并返回 dictionary
中最长的字符串,该字符串可以通过删除 s
中的某些字符得到。
如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
输入:s = "abpcplea", dictionary = ["ale","apple","monkey","plea"] 输出:"apple"
思路:
遍历字符字典中的字符串,找到s串的子串,然后计算长度;
t串的长度大于res的长度,更新res;
t串的长度==res串的长度并且t串的字母序是靠前的,更新res;
注意:
使用String类中的compareTo()方法
返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。
代码:
class Solution {
public String findLongestWord(String s, List<String> dictionary) {
String res = "";
for (String str : dictionary) {
int i = 0;
int j = 0;
while (i < s.length() && j < str.length()) {
if (s.charAt(i) == str.charAt(j)) {
j++;
}
i++;
}
if (j == str.length()) {
if ((str.length() > res.length() || str.length() == res.length() && str.compareTo(res) < 0)) {
// 说明是子串 然后判断长度
res = str;
}
}
}
return res;
}
}
二、最长特殊序列II
输入: strs = ["aba","cdc","eae"] 输出: 3
输入: strs = ["aaa","aaa","aa"] 输出: -1
题意:
给定字符串列表 strs
,返回其中 最长的特殊序列 的长度。如果最长特殊序列不存在,返回 -1
。
最长的特殊序列:该序列为某字符串 独有的子序列(即不能是其他字符串的子序列)。
思路:
就是判断每一个字符串是不是其它字符串的子串,如果是的话,该字符串的最长特殊序列的长度为0;如果都不是的话,那么最长特殊序列的长度为str.length();
代码:
class Solution {
public int findLUSlength(String[] strs) {
int max = -1;
for (int i = 0; i < strs.length; i++) {
boolean flag = true;
String t = strs[i];
for (int j = 0; j < strs.length; j++) {
if (i != j) {
String s = strs[j];
System.out.println("t: "+t+" s: "+s+"是否为子串:"+isSonStr(t,s));
if (isSonStr(t, s)) {
flag = false;
}
}
}
if (flag)
max = Math.max(max, strs[i].length());
}
return max;
}
public boolean isSonStr(String t, String s) {
int i = 0;
int j = 0;
while (i < t.length() && j < s.length()) {
if (t.charAt(i) == s.charAt(j)) {
i++;
}
j++;
}
if (i == t.length())
return true;
return false;
}
}
疑惑:
如果是aaa,aaa,aa。当进行判断字符串aaa是不是最长特殊序列的时候。先和aaa比较,发现是子串,flag直接为false;就不用和后面的aa进行比较了(尽管比较返回结果为true);
只要它是剩下字符串中其中一个的子串,就直接flag=false;
三、加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。
代码十分巧妙:如果该位置是9的话,那么就把它变成0。(数组的其他位置还会发生变化,需要继续遍历,直到不==9,返回)
如果不是9的话,就直接+1并且return digits;因为此时数组其他位置上已经不会变化了,直接返回数组就行。
最后如果还没有return,那么就要在最开始进一位
代码:
class Solution {
public int[] plusOne(int[] digits) {
int size=digits.length;
for(int i=size-1;i>=0;i--){
if(digits[i]==9){
digits[i]=0;
}else{
digits[i]++;
return digits;
}
}
//如果代码执行到这里 说明还没有return掉 遇到999这种情况
int[] arr=new int[digits.length+1];
arr[0]=1;
return arr;
}
}
四、字符串相乘
如何避免越界的情况:
之前我使用int类型的变量去累加每一位上相乘的结果,因此会越界异常。
每一位置上的数字使用数组存储,然后再用字符串将每一个位置上的数字拼接起来。
思路:
1.首先创建一个长度为len1+len2的数组,用于存放每一位上的数。m位数*n位数的结果最多是(m+n)位数
2.使用双重循环给数组的每一个位置上赋值。int multi=x*y+arr[i+j+1]; arr[i+j+1]=multi%10;
arr[i+j]=multi/10; i+j+1是根据i,j的大小决定的,并且表示的是multi个位上的数字。multi是x*y的乘积加上上一次相乘结果的进位。i+j+1是进位。
注意:
最后使用StringBuilder拼接的时候,首位可能为0(比如说33*30=999 只有三位数 但是数组的空间是4),要考虑到这种情况
代码:
//会涉及到很大的数 超出int的范围
class Solution {
public String multiply(String num1, String num2) {
//如果出现0 直接返回0
if(num1.equals("0")||num2.equals("0"))return "0";
//其他情况
int len1=num1.length();
int len2=num2.length();
int[] res=new int[len1+len2];//len1位数*len2位数 结果最多会有(len1+len2)位数
for(int i=len1-1;i>=0;i--){
int value1=num1.charAt(i)-'0';
for(int j=len2-1;j>=0;j--){
int value2=num2.charAt(j)-'0';
int multi=value1*value2+res[i+j+1];
res[i+j+1]=multi%10;
res[i+j]+=multi/10;
}
}
StringBuilder sb=new StringBuilder();
for(int i=0;i<res.length;i++){
if(i==0&&res[i]==0)continue;
sb.append(res[i]);//拼接每一个数字
}
return sb.toString();
}
}
五、累加数(回溯+剪枝)
累加数 是一个字符串,组成它的数字可以形成累加序列。
一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和。
给你一个只包含数字 '0'-'9'
的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回 true
;否则,返回 false
。
说明:累加序列里的数,除数字 0 之外,不会 以 0 开头,所以不会出现 1, 2, 03
或者 1, 02, 3
的情况。101 这样也可以
回溯算法:
1.返回值:boolean 参数:String num(字符串)、int count(数字的个数)、int startIndex(遍历的起始位置)、int prev(前一个数)、int prevprev(前两个数)
2.终止条件:当startIndex>=num.length()的时候,说明遍历结束了。此时要判断count是否大于2,如果超过两个数字,就说明字符串中存在三个数字,符合x1+x2=x3这样的规律
3.单层递归逻辑:for循环中,每次根据遍历的位置计算当前数字的值。然后根据条件判断是否继续遍历(条件为:count>=2 然后current>prev+prevprev 或者current<prev+prevprev)。如果满足条件,就终止了(遍历到下一层自动终止了)
代码:
class Solution {
public boolean isAdditiveNumber(String num) {
return dfs(num,0,0,0,0);
}
public boolean dfs(String num,int count,int startIndex,long prev,long pprev){
//终止条件
if(startIndex>=num.length()){
return count>2;
}
//单层递归逻辑
long cur=0;
for(int i=startIndex;i<num.length();i++){
//剪枝1:如果碰到0开头的数字 直接return false;
if(i>startIndex&&num.charAt(startIndex)=='0')return false;
char ch=num.charAt(i);
int number=ch-'0';
cur=cur*10+number;//当前的数字
if(count>=2){
long sum=prev+pprev;
if(cur>sum){
return false;
}else if(cur<sum){
continue;
}
}
//如果count<2或者满足条件 就需要继续遍历
boolean flag=dfs(num,count+1,i+1,cur,prev);
if(flag)return true;
}
return false;
}
}
六、z字形变换
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
思路:
使用List<StringBuilder>来存储每一行的字符串,最后将每一行的字符串拼接起来返回。
在遍历每一个字符串的字符的同时,把它加到对应的行中。(行是从上往下依次递增,如果在第一行或者最后一行就要转变方向 往下是++ 往上是--)
代码:
class Solution {
public String convert(String s, int numRows) {
//如果按照一行变换的话 直接返回
if(numRows==1)return s;
//创建存储行的集合
List<StringBuilder> rows=new ArrayList<>();
//初始化
for(int i=0;i<Math.min(s.length(),numRows);i++){
rows.add(new StringBuilder());
}
//控制方向的变量
boolean up=false;
int curRow=0;
for(char ch:s.toCharArray()){
rows.get(curRow).append(ch);//某一行进行拼接
if(curRow==0||curRow==numRows-1){
up=!up;
}
curRow+=up?1:-1;
}
StringBuilder res=new StringBuilder();
for(StringBuilder sb:rows){
res.append(sb);
}
return res.toString();
}
}
七、回文子串类问题(二维dp)
1.dp[i][j]:a.从i->j这个范围内是否是回文子串。b.或者i->j里面最大回文子串的长度。
2.递推公式:
a.如果str.charAt(i)==str.charAt(j)。如果j-i<=1,dp[i][j]=true;如果j-i>1,dp[i][j]是否是回文子串需要看dp[i+1][j-1];
b.如果str.charAt(i)==str.charAt(j)。dp[i][j]=dp[i+1][j-1]+2; 如果不相等,dp[i][j]=Math.max(dp[i][j-1],dp[i+1][j])
八、最大回文数乘积()
给定一个整数 n ,返回 可表示为两个 n
位整数乘积的 最大回文整数 。因为答案可能非常大,所以返回它对 1337
取余 。
输入:n = 2 输出:987 解释:99 x 91 = 9009, 9009 % 1337 = 987
9009是2位数整数乘积的最大回文整数。
思路:
1.根据n的大小得出最大的n位数
2.从最大的n位数从大到小依次构建相应的回文数
3.如果回文数可以由两个n位数相乘得来,那么就return 回文数%1337
代码:
class Solution {
public int largestPalindrome(int n) {
if(n==1)return 9;
//找到n位数字可以表示的最大值
int max=(int)Math.pow(10,n)-1;
for(int i=max;i>=0;i--){
long num=i;
long t=i;
//找数的最大值
while(t!=0){
num=num*10+t%10;
t/=10;
}
//找最大的回文数
for(long j=max;j*j>=num;j--){
if(num%j==0)return (int)(num%1337);
}
}
return -1;
}
}
九、数字的补数(位运算)
对整数的二进制表示取反(0
变 1
,1
变 0
)后,再转换为十进制表示,可以得到这个整数的补数。
例如,整数 5
的二进制表示是 "101"
,取反后得到 "010"
,再转回十进制表示得到补数 2
。
位运算:(异或)
000^111=111 (异或),只有当相同的位置上的数字不同时,得1;否则,得0;
思路:
这道题要我们求补数,就是让我们将num上的每一位有效值(不算前面补的0)都和1做异或运算,那么有多少位有效数字呢?就是num/2 可以被除几次。每除一次,就拼接一次1(二进制上的1)
代码:
class Solution {
public int findComplement(int num) {
int temp=num,c=0;
//首先看temp有多少位
while(temp>0){
temp>>=1;//右移一位
c=(c<<1)+1;//c每次都左移一位
}
return num^c;
}
}
十、汉明距离(位运算)
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x
和 y
,计算并返回它们之间的汉明距离。
输入:x = 1, y = 4 输出:2 解释: 1 (0 0 0 1) 4 (0 1 0 0)
思路:
把xy异或之后 有多少个1 就有多少个二进制位不同的位置。(只有不同的二进制位异或后才会得1)。然后计算1的个数就可以。
(z&1)==1 count++;z>>>1;
代码:
class Solution {
public int hammingDistance(int x, int y) {
//异或一下 如果找为1的最小距离
int c=x^y;
int start=0,end=0;
int count=0;
while(c!=0){
if((c&1)==1)count++;
c>>>=1;//因为c可能是0 >>>不考虑符号位
}
return count;
}
}