把敬业变成习惯。短期来看是为了雇主,长期来看是为了自己。
1.题目:重复的字符串
/**
* 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
*
* 输入: "abab"
* 输出: True
* 解释: 可由子字符串 "ab" 重复两次构成。
*
* 输入: "aba"
* 输出: False
*/
这道题整体的思路大体有两种,第一种从长度为1开始分割字符串,然后用分割后的字符串去拼接k次,最后比较字符串;第二种其实还是分割字符串,不过是从整体二等分开始,三等分,直到N等分,然后拼接比较。
但要注意的一点是,字符串长度不超过10000,也就是说字符串可能会有7000,8000这么长,你在这里需要加上一个过滤,如果字符串长度对分割长度取余不等于零,那么注定拼接不成目标字符串,应当直接过滤;还有就是注意拼接的方式,最好使用StringBuilder来拼接,因为这种方式比直接字符串拼接效率上有优势。
public static boolean repeatedSubstringPattern(String s) {
int length=s.length();
for (int i=1;i<=length/2;i++){
if (length%i!=0) continue;
int k=length/i;
String string=s.substring(0,i);
StringBuilder a=new StringBuilder();
for (;k>0;k--){
a.append(string);
}
System.out.println(a);
if (a.toString().equals(s)){
return true;
}
}
return false;
}
2.最小叠加次数
/**
* 给定两个字符串 A 和 B, 寻找重复叠加字符串A的最小次数,使得字符串B成为叠加后的字符串A的子串,如果不存在则返回 -1。
* 举个例子,A = "abcd",B = "cdabcdab"。
* 答案为 3, 因为 A 重复叠加三遍后为 “abcdabcdabcd”,此时 B 是其子串;A 重复叠加两遍后为"abcdabcd",B 并不是其子串。
*/
如果B的长度小于A,那么有三种情况,要么A不用叠加,B就已经是A的子串,要么就加个头或者围,比如A是aaaaaaaaaab,B是ba,要么就是同时加上头和尾,就像题目中给的例子一样
如果B的长度大于A,那么A就要至少叠加两者长度之比的倍数,最多也就是这个倍数加上2
要注意一种情况就是无论A如何叠加,B都不是A的子串,也就是B中有A中没有出现的字符,那么我们可以使用indexOf判断,因为要么A长度大于B,就判断B是不是三个A加起来的子串,要么B长度大于A,判断A是不是三个B加起来的子串
public static int repeatedStringMatch(String A, String B) {
if ((B+B+B).indexOf(A)==-1&&(A+A+A).indexOf(B)==-1){
return -1;
}
int lengthA=A.length();
int lengthB=B.length();
int multiple=lengthB/lengthA;
for (int i=multiple;i<multiple+3;i++){
StringBuilder stringBuilder=new StringBuilder();
for (int j=0;j<i;j++){
stringBuilder.append(A);
}
if (stringBuilder.toString().indexOf(B)!=-1){
return i;
}
}
return -1;
}
还有一种思路便是将两者的字符循环添加到hashset中,然后进行比较看看是否可达成条件,这样写的代码虽然代码多一些但是效率会高不少,因为字符串拼接很耗时而且indexOf的内部实现也是for循环做的
public static int repeatedStringMatch(String A, String B) {
if (A.indexOf(B)!=-1){
return -1;
}
int lengthA=A.length();
int lengthB=B.length();
String newB=B.replaceAll(A,"");
HashSet<String> hashSet=new HashSet<String>();
for (int i=0;i<lengthA;i++){
hashSet.add(A.substring(i,i+1));
}
HashSet<String> hashSet2=new HashSet<String>();
for (int i=0;i<lengthB;i++){
hashSet2.add(B.substring(i,i+1));
}
for (String s:hashSet2){
if (!hashSet.contains(s)){
return -1;
}
}
int multiple=lengthB/lengthA;
for (int i=multiple;i<multiple+3;i++){
StringBuilder stringBuilder=new StringBuilder();
for (int j=0;j<i;j++){
stringBuilder.append(A);
}
if (stringBuilder.toString().indexOf(B)!=-1){
return i;
}
}
return -1;
}
3.幂次方
这是一个系列的问题,给定一个整数,判别它是否是2的幂次方
是不是2的幂次方是最好判断的,因为凡是2的幂次方的数转换成二进制都有一个特点,就是前面一个1,后面跟n个0,
2:10
4:100;
8:1000;
16:10000;
我们只需要知道里面一共有多少个1就行了
需要注意的是负数全不是2的幂次方
public static boolean isPowerOfTwo(int n) {
if (n<=0) return false;
int x=Integer.toBinaryString(n).indexOf("1");
int y=Integer.toBinaryString(n).lastIndexOf("1");
if (x==y){
return true;
}else {
return false;
}
}
更快的版本
public static boolean isPowerOfTwo(int n) {
if (n<=0) return false;
int num=0;
char[] chars=Integer.toBinaryString(n).toCharArray();
for (int i=0;i<chars.length;i++){
if (chars[i]=='1'){
num++;
}
}
if (num!=1){
return false;
}else {
return true;
}
}
/**
* 给定一个整数,写一个函数来判断它是否是 3 的幂次方。
* 输入: 27
* 输出: true
* 输入: 0
* 输出: false
*/
判断是不是3的幂次方就不能在二进制上找灵感了,但是3的幂次方对3取余一定等于0,且除以三的结果对3取余也是零
需要注意的一就是小于等于0的数都不是3的次幂,1是任何数的0次方;
public static boolean isPowerOfThree(int n) {
if (n<=0) return false;
if (n==1) return true;
while (n!=3){
if (n%3!=0){
return false;
}
n/=3;
}
return true;
}
/**
* 给定一个整数,写一个函数来判断它是否是 4 的幂次方。
*/
对于4的判断我们可以沿用3的方法,也可以在二进制上发现规律,因为一个数是4的幂次方那它就一定是2的幂次方,从二进制上你会发现除了1,2以外,凡是4的倍数都只有一个1和偶数个0;(但是leetcode上说2不是4的幂次方,我不明白为什么)
public static boolean isPowerOfFour(int num) {
if (num==1) return true;
if (num<=3) return false;
while (num!=4){
if (num%4!=0){
return false;
}
num/=4;
}
return true;
}
if (num==1) return true;
if (num<=3) return false;
int one=0;
int zero=0;
char[] chars=Integer.toBinaryString(num).toCharArray();
for (int i=0;i<chars.length;i++){
if (chars[i]=='1'){
one++;
}
if (chars[i]=='0'){
zero++;
}
}
if (one!=1){
return false;
}else if (zero%2==0){
return true;
}
return false;
4.回文数
/**
* 给定一个数,判断是不是一个回文数
*/
这个大家都应该做过,只要比较前一半数是不是和后一半数相等就可以了,下面这种方法用到了java自带的string.toCharArray()
public static boolean isPalindrome(int x) {
char[] chars=(x+"").toCharArray();
for (int i=0;i<chars.length/2 ;i++){
if (chars[i]!=chars[chars.length-1-i]){
return false;
}
}
return true;
}
如果我们不借助这个方法该如何做呢?回文数就是后面等于前面嘛,我们把后边拿出来和前面比一比就可以了,奇数位数的话就把最后一位除10抹掉。我们还可以省去求数字长度的步骤,因为不断取后边的数字拼接,这个也在不断减小,如果拼接的数大于等于这个数的时候就可以停止了。在开始还可以略去两种情况,负数肯定不是回文数,大于0的是10的倍数的数肯定不是回文数
public boolean isPalindrome(int x) {
if(x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while(x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
return x == revertedNumber || x == revertedNumber/10;
}
这样就可以了,我们做算法题的时候一定要把注意力放到算法思想上,这样才能学到更多
/**
* 给定一个链表,判断是不是一个回文链表
* 输入: 1->2
* 输出: false
* 输入: 1->2->2->1
* 输出: true
*/
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
现在整数换成了链表,如何证明这个链表是不是一个回文链表呢?
我们可以先遍历这个链表,把其中的每个数添加到列表里,然后像判断回文数一样判断这个列表
当然,我们还可以借用数据结构的力量,要把一串信息倒过来用栈来做非常合适,我们定义两个变量保存链表的初始状态,也有人叫它们快慢指针,思想都一样,使用一个遍历链表并添加到栈中,然后遍历另一个链表并与栈中pop()出来的节点进行比对,全一样那就说明这个链表调过来和原链表相同,是个回文链表,有一个不是就不是回文链表;
Stack<ListNode> stack=new Stack<>();
ListNode slow=head;
ListNode fast=head;
if(fast==null||fast.next==null)//0个节点或是1个节点
return true;
stack.push(slow);
while(fast.next!=null&&fast.next.next!=null)
{
fast=fast.next.next;
slow=slow.next;
stack.push(slow);
}
if(fast.next!=null)//链表长度为偶数
slow=slow.next;
ListNode cur=slow;
while(cur!=null)
{
if(cur.val!=stack.pop().val)
return false;
cur=cur.next;
}
return true;