常用算法

最长递增子序列长度

给你一个整数数组 nums,找到其中最长严格递增子序列的长度。

 

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

 

解题思路:找出序列中最长子序列的长度

1.动态规划法

 

动态规划从前一状态转移到后一状态

为了形成更长的上升子序列,在以往子序列中寻找最大的值

 

 

 

 

回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 

回文链表从字符串正反两边读起,得到的字符内容都是相等的。

 

思路1:暴力枚举法(空间复杂度On))

取出单链表中的所有的数,装在List中,再二分法判断相等

 

思路2放在两个字符串中,判断两个字符串是否相等

/**

 * Definition for singly-linked list.

 * public class ListNode {

 *     int val;

 *     ListNode next;

 *     ListNode() {}

 *     ListNode(int val) { this.val = val; }

 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }

 * }

 */

class Solution {

    public boolean isPalindrome(ListNode head) {

        String a1="";

        String a2="";

 

        if (head == null){

            return false;

        }

        while(head != null){

            a1 = a1 + head.val;

            a2 = head.val + a2;

            head = head.next;

        }

        if(a1.equals(a2)){

            return true;

        }

        else{

            return false;

        }

    }

}

思路三

快指针走到末尾,慢指针刚好到中间。其中慢指针将前半部分反转。然后比较。

要找单链表的中间节点,用快慢指针最好,只用遍历一遍

慢指针一次走一步快指针一次走两步找到中心点(通过快慢指针找到中心点)

快指针和慢指针从同一位置点出发

/**

 * Definition for singly-linked list.

 * public class ListNode {

 *     int val;

 *     ListNode next;

 *     ListNode() {}

 *     ListNode(int val) { this.val = val; }

 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }

 * }

 */

class Solution {

    public boolean isPalindrome(ListNode head) {

        ListNode kuai = head, pre = null, preper = null;

        ListNode man = head;

 

        pre = head;

 

        while(kuai != null && kuai.next != null){

            // 将慢指针经过的地方进行链表翻转

            // 链表存的都是地址,真正改变的是next的值

            pre = man;

 

            man = man.next;

            kuai = kuai.next.next;

 

            pre.next = preper;

            preper = pre;

        }

// man前半部分链表的尾结点

        if(kuai != null){

            man = man.next;

        }

 

        while(pre != null && man != null){

            if(pre.val != man.val){

                return false;

            }

            pre = pre.next;

            man = man.next;

        }

        return true;

    }

}

 

链表翻转

 

链表翻转是对链表的指向进行操作不是移动链表本身的元素

 

搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值互不相同。

 

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

 

给你旋转后的数组nums和一个整数target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 

 

思路1:暴力枚举法  时间复杂度On

class Solution {

    public int search(int[] nums, int target) {

 

        for(int i=0;i<nums.length;i++){

            if(nums[i] == target){

                return i;

            }

        }

        return -1;

    }

}

思路2这道题和平常二分法查找的不同就在于,把一个有序递增的数组分成了,两个递增的数组,我们需要做的就是判断这个数在哪一个递增的数组中,然后再去用常规的二分法去解决

// 1. 首先明白,旋转数组后,从中间划分,一定有一边是有序的。

// 2. 由于一定有一边是有序的,所以根据有序的两个边界值来判断目标值在有序一边还是无序一边

// 3. 这题找目标值,遇到目标值即返回

// 4. 注意:由于有序的一边的边界值可能等于目标值,所以判断目标值是否在有序的那边时应该加个等号(在二分查找某个具体值得时候如果把握不好边界值,可以再每次查找前判断下边界值,也就是while循环里面的两个if注释)

class Solution {

    public int search(int[] nums, int target) {

        int start=0, end=nums.length-1;

 

        // 采用二分查找法

        while(start <= end){

            int mid = (start + end)/2;

            if(nums[mid] == target){

                return mid;

            }

            // 先判断两边 哪一边是有序 哪一边是无序

            else if(nums[0] <= nums[mid]){

                // 左侧有序

                if(nums[mid] > target && target >= nums[start]){

                    end = mid - 1;

                }

                else{

                    start = mid + 1;

                }

            }

            else{

                // 右侧有序

                if(nums[mid] < target && target <= nums[end]){

                    start = mid + 1;

                }

                else{

                    end = mid - 1;

                }

            }

        }

        return -1;

    }

}

 

多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

 

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

 

思路一暴力枚举法。用map记录下所有元素出现的次数

 

 

二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明叶子节点是指没有子节点的节点。

 

思路一动态规划法或递归

max{左子树的深度, 右子树的深度}

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public int maxDepth(TreeNode root) {

        if(root == null){

            return 0;

        }

        

        int leftDepth = 0;

        int rightDepth = 0;

 

        if(root.left != null){

            leftDepth = maxDepth(root.left);

        }

        

        if(root.right != null){

            rightDepth = maxDepth(root.right);

        }

 

        return Math.max(leftDepth+1,rightDepth+1);

    }

}

 

最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

 

思路一递归法

class Solution {

    public int minPathSum(int[][] grid) {

        return pathSum(0,0,grid);

 

    }

 

    public int pathSum(int i, int j, int[][] grid){

        int length = grid.length;

        int width = grid[0].length;

 

        int lw = 0;

        int ll = 0;

 

        if(i+1 < length){

            ll = pathSum(i+1,j,grid);

        }

        else{

            ll = -1;

        }

 

        if(j+1 < width){

            lw = pathSum(i,j+1,grid);

        }

        else{

            lw = -1;

        }

 

        if(lw >= 0 && ll >= 0){

            return Math.min(lw,ll) + grid[i][j];

        }

        else if(lw < 0 && ll < 0){

            return grid[i][j];

        }

        else if(lw < 0){

            return ll + grid[i][j];

        }

        else{

            return lw + grid[i][j];

        }

    }

}

思路二:动态规划法

class Solution {

    public int minPathSum(int[][] grid) {

        

        for(int i=0;i<grid.length;i++){

            for(int j=0;j<grid[0].length;j++){

                if(j==0 && i== 0){

                    continue;

                }

                else if(i-1 >= 0 && j-1>=0){

                    grid[i][j] = Math.min(grid[i-1][j],grid[i][j-1]) + grid[i][j];

                }

                else if(i-1 < 0){

                    grid[i][j] = grid[i][j-1] + grid[i][j];

                }

                else{

                    grid[i][j] = grid[i-1][j] + grid[i][j];

                }

            }

        }

        return grid[grid.length-1][grid[0].length-1];

    }

}

 

盛最多水的容器

给定一个长度为 n的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)  (i, height[i]) 

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

 

思路一采用暴力枚举法计算每个点的最大容纳量

缺点耗时长

class Solution {

    public int maxArea(int[] height) {

        int out = 0;

        for(int i=0; i<height.length; i++){

            for(int j=i+1; j<height.length; j++){

                out = Math.max(out, Math.min(height[i],height[j]) * (j-i));

            }

        }

        return out;

    }

}

思路二:双向双指针法

 

在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 -1−1​ 变短:

 

向内 移动短板 ,水槽的短板可能变大,因此下个水槽的面积 可能增大 

若向内 移动长板 ,水槽的短板不变或变小,因此下个水槽的面积 一定变小 。

 

因此,初始化双指针分列水槽左右两端循环每轮将短板向内移动一格,并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积

class Solution {

    public int maxArea(int[] height) {

        int out = 0;

        int flag1 = 0, flag2 = height.length-1;

 

        while(flag1 < flag2){

            if(height[flag1] < height[flag2]){

                out = Math.max(out, (flag2-flag1)*height[flag1]);

                flag1++;

            }

            else{

                out = Math.max(out, (flag2-flag1)*height[flag2]);

                flag2--;

            }

        }

        return out;

    }

}

回文子串 +0320++++++++++++++++

给你一个字符串s,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

 

思路一暴力枚举法   

列出所有的子串

判断自串是否为回文子串(从开始和结束位置采用双指针进行判断)

 

思路二:采用动态规划法-----遍历I j 记录之前的操作

 

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;

    }

}

 

思路三中心扩展法

class Solution6472 {

    public int countSubstrings(String s) {

        // 中心扩展法

        int ans = 0;

        for (int center = 0; center < 2 * s.length() - 1; center++) {

            // leftright指针和中心点的关系是?

            // 首先是left,有一个很明显的2倍关系的存在,其次是right,可能和left指向同一个(偶数时),也可能往后移动一个(奇数)

            // 大致的关系出来了,可以选择带两个特殊例子进去看看是否满足。

            int left = center / 2;

            int right = left + center % 2;

 

            while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {

                ans++;

                left--;

                right++;

            }

        }

        return ans;

    }

}

汉明距离

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 x  y,计算并返回它们之间的汉明距离。

 

思路一直接异或计算1的个数(异或 加 移位)

java 异或 ^   移位 >>

class Solution {

    public int hammingDistance(int x, int y) {

        int flag = 0;

        int out = x^y;

        while(out>0){

            if(out % 2 != 0){

                flag++;

            }

            out = out / 2;

        }

        return flag;

    }

}

 

思路二

class Solution {

    public int hammingDistance(int x, int y) {

        int s = x ^ y, ret = 0;

        while (s != 0) {

            ret += s & 1;

            s >>= 1;

        }

        return ret;

    }

}

任务调度器+0321++++++++++++++++

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

 

然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

 

你需要计算完成所有任务所需要的 最短时间

 

 

思路一:

存在空闲时间和不存在空闲时间两种状态

 

 

class Solution {

    public int leastInterval(char[] tasks, int n) {

        int[] zz = new int[26];

        int taskNum = 0;

        int maxTaskNum = 0;

 

        for(char cc:tasks){

            zz[cc - 'A']++;

            taskNum = Math.max(taskNum, zz[cc - 'A']);

        }

 

        for(int flag:zz){

            if(flag == taskNum){

                maxTaskNum++;

            }

        }

 

        return Math.max(tasks.length, (taskNum - 1)*(n+1) + maxTaskNum);

    }

}

 

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以 1  个台阶。你有多少种不同的方法可以爬到楼顶呢?

 

思路一:

 

思路二动态规划法

 

class Solution {

    public int climbStairs(int n) {

        int[] dp = new int[n+1];

        dp[0] = 1;

        dp[1] = 1;

 

        for(int i=2; i <= n; i++){

            dp[i] = dp[i-1] + dp[i-2];

        }

        return dp[n];

    }

}

 

 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 

DLR--前序遍历(根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面

LDR--中序遍历(根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面)

LRD--后序遍历(根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面)

 

 

思路一:采用递归法 进行输出

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> inorderTraversal(TreeNode root) {

        List<Integer> out = new ArrayList<Integer>();

        List<Integer> leftOut = new ArrayList<Integer>();

        List<Integer> rightOut = new ArrayList<Integer>();

 

        if(root == null){

            return out;

        }

 

        if(root.left != null){

            leftOut = inorderTraversal(root.left);

        }

 

        if(root.right != null){

            rightOut = inorderTraversal(root.right);

        }

 

        out.addAll(leftOut);

        out.add(root.val);

        out.addAll(rightOut);

        return out;

    }

}

删除链表的倒数第 N 个结点

 

 

思路一:

采用两遍遍历

第一遍找链表总长度

第二遍删除倒数第n个节点

 

思路二:

采用双指针法--------一遍遍历

两指针间隔n个节点,每次都步进1

/**

 * Definition for singly-linked list.

 * public class ListNode {

 *     int val;

 *     ListNode next;

 *     ListNode() {}

 *     ListNode(int val) { this.val = val; }

 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }

 * }

 */

class Solution {

    public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode fast = head;

        ListNode slow = null;

        int flag = 1;

 

        while(fast.next != null){

            fast = fast.next;

            

            if(flag == n){

                slow = head;

            }

            else if(flag > n){

                slow = slow.next;

            }

            flag++;

        }

 

        if(slow != null ){

            if(slow.next != null){

                slow.next = slow.next.next;

            }

            else{

                slow.next = null;

            }

             return head;

        }

       else{

           return head.next;

       }

    }

}

 

统计封闭岛屿的数目

二维矩阵 grid  (土地)和 (水)组成。岛是由最大的4个方向连通的 组成的群,封闭岛是一个 完全 1包围(左、上、右、下)的岛。

请返回 封闭岛屿 的数目。

 

 

思路一:采用深度优先遍历

四个方向深搜, 有一个方向越界就不是封闭的

必须要将四个方向都跑完,不能遇到一个为false就返回

 

使用深度优先搜索方法。从土地即0的位置进行深搜,在深搜过程中,每次搜索到0位置时将其改成1防止重复搜索。本题难点是如何判断搜索到的岛屿是否为封闭岛屿,可以不难发现,只要是封闭岛屿,那么就不会搜索到矩阵的边界,通过这一个条件,可以在搜索过程中返回一个bool值,表示该岛屿是否为封闭岛屿。

 

class Solution {

    public int closedIsland(int[][] grid) {

        int num = 0;

 

        if(grid.length < 3 || grid[0].length < 3){

            return num;

        }

 

        for(int i=1; i < grid.length - 1; i++){

            for(int j=1; j < grid[0].length - 1; j++){

                if(grid[i][j] == 0){

                    if(dfs(grid, i, j)){

                        num++;

                    }

                }

            }

        }

        return num;

    }

 

    public boolean dfs(int[][] grid, int i, int j){

 

        if((i == 0 || i == grid.length - 1 || j == 0 || j == grid[0].length - 1) && grid[i][j] == 0){

            return false;

        }

// 防止回头搜索已经搜索过的 两个岛屿将必定间隔1

        grid[i][j] = 1;

 

        int[] dx = {-1, 0, 1, 0};

        int[] dy = {0, -1, 0, 1};

        boolean out = true;

 

        for(int k=0; k<4; k++){

            int ii = i + dx[k];

            int jj = j + dy[k];

            if(ii < 0 || ii > grid.length - 1 || jj < 0 || jj > grid[0].length - 1 || grid[ii][jj] == 1){

                continue;

            }

            out = out & dfs(grid, ii, jj);

        }

        return out;

    }

}

 

将数组分成和相等的三个部分

给你一个整数数组 arr,只有可以将其划分为三个和相等的 非空 部分时才返回 true,否则返回 false

形式上,如果可以找出索引 i + 1 < j 且满足 (arr[0] + arr[1] + ... + arr[i] == arr[i + 1] + arr[i + 2] + ... + arr[j - 1] == arr[j] + arr[j + 1] + ... + arr[arr.length - 1]) 就可以将数组三等分。

 

思路一:看能不能将数组进行三等分 采用贪心法 找最小的数组长度

class Solution {

    public boolean canThreePartsEqualSum(int[] arr) {

        int out = 0;

        int out2 = 0;

        for(int i=0; i < arr.length; i++){

            out = out + arr[i];

        }

 

        int flag = out / 3;

        int flag2 = 0;

 

        if(out % 3 == 0 && arr.length >= 3){

            for(int i=0; i < arr.length - 1; i++){

 

                out2 = out2 + arr[i];

                if(out2 == flag){

                    flag2++;

                    out2 = 0;

                }

                // 具备跳出机制 采用贪心法 找最小的数组长度

                if(flag2 == 2){

                    return true;

                }

            }

        }

        return false;

    }

}

 

思路二使用双指针,从数组两头开始一起找,节约时间

左右两边都等于 sum/3 ,中间也一定等于

 

 

 将数组拆分成斐波那契序列

给定一个数字字符串 num,比如 "123456579",我们可以将它分成「斐波那契式」的序列 [123, 456, 579]

 

形式上,斐波那契式 序列是一个非负整数列表 f,且满足:

 

0 <= f[i] < 231 ,(也就是说,每个整数都符合 32  有符号整数类型)

f.length >= 3

对于所有的0 <= i < f.length - 2,都有 f[i] + f[i + 1] = f[i + 2]

另外,请注意,将字符串拆分成小块时,每个块的数字一定不要以零开头,除非这个块是数字 0 本身。

 

返回从 num 拆分出来的任意一组斐波那契式的序列块,如果不能拆分则返回 []

 

思路一:回朔法+剪枝法

根据斐波那契式序列的要求,从第 33 个数开始,每个数都等于前 22 个数的和,因此从第 33 个数开始,需要判断拆分出的数是否等于前 22 个数的和,只有满足要求时才进行拆分,否则不进行拆分。

 

 

交换和

给定两个整数数组,请交换一对数值(每个数组中取一个数值),使得两个数组所有元素的和相等。

 

返回一个数组,第一个元素是第一个数组中要交换的元素,第二个元素是第二个数组中要交换的元素。若有多个答案,返回任意一个均可。若无满足条件的数值,返回空数组。

 

思路:

 

(建立公式进行分析)

以一个值为出发点找另一个数组中找满足条件的值

 

难点在另一个数组中的查找上面(进行排序,再二分查找)

 

Arrays.sort(array1)

利用Set的快速查找, 同时set具有不可重复性

手写快排

class Solution {

    public int[] findSwapValues(int[] array1, int[] array2) {

        int num1 = 0,num2 = 0;

        // 利用set记录出现的数据

        Set<Integer> container = new HashSet<>();

 

        for(int j : array1){

            num1 += j;

        }

        for(int j : array2){

            num2 += j;

            container.add(j);

        }

 

        if((num1 + num2) % 2 != 0){

            return new int[]{};

        }

 

        for(int j : array1){

            if(container.contains(j + (num2-num1)/2)){

                return  new int[]{j, j + (num2-num1)/2};

            }

        }

        return new int[]{};

    }

}

 

写字符串需要的行数

我们要把给定的字符串 从左到右写到每一行上,每一行的最大宽度为100个单位,如果我们在写某个字母的时候会使这行超过了100 个单位,那么我们应该把这个字母写到下一行。我们给定了一个数组 widths ,这个数组 widths[0] 代表 'a' 需要的单位, widths[1] 代表 'b' 需要的单位,... widths[25] 代表 'z' 需要的单位。

 

现在回答两个问题:至少多少行能放下S,以及最后一行使用的宽度是多少个单位?将你的答案作为长度为2的整数列表返回。

 

思路1:遍历整个字符串进行 行统计 以及 最后行的单位

class Solution {

    public int[] numberOfLines(int[] widths, String s) {

        char[] cc = s.toCharArray();

        int len = 0, num = 1;

 

        for(char c : cc){

            int val = widths[c - 'a'];

 

            if(len + val > 100){

                num += 1;

                len = val;

            }

            else{

                len += val;

            }

        }

        return new int[]{num,len};

 

    }

}

思路二

class Solution {

    public static final int MAX_WIDTH = 100;

 

    public int[] numberOfLines(int[] widths, String s) {

        int lines = 1;

        int width = 0;

        for (int i = 0; i < s.length(); i++) {

            int need = widths[s.charAt(i) - 'a'];

            width += need;

            if (width > MAX_WIDTH) {

                lines++;

                width = need;

            }

        }

        return new int[]{lines, width};

    }

}

 

检查数组对是否可以被 k 整除

给你一个整数数组 arr 和一个整数 k ,其中数组长度是偶数,值为 n

现在需要把数组恰好分成 n / 2 对,以使每对数字的和都能够被 k 整除。

如果存在这样的分法,请返回 True ;否则,返回 False

 

思路:

 

利用余数进行配对,对于第一次取余小于0的元素来说,只要再加上一个k就能使其大于0

class Solution {

    public boolean canArrange(int[] arr, int k) {

 

        List<Integer> list = new ArrayList<>();

 

        for(int i : arr){

            int flag;

            if(i % k < 0){

                // 针对数组中存在小于0的数

                flag = (i % k + k) % k;

            }

            else{

                flag = i % k;

            }

 

            if(flag > 0){

                list.add(flag);

            }

        }

 

        if(list.size() % 2 != 0){

            return false;

        }    

 

        Collections.sort(list);

 

        for(int i=0; i<list.size()/2; i++){

            if(list.get(i) + list.get(list.size() - i - 1) != k){

                return false;

            }

        }

        return true;

    }

}

 重复叠加字符串匹配+0327++++++++++++++++

给定两个字符串 a b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 子串,如果不存在则返回 -1

 

注意:字符串 "abc" 重复叠加 0 次是 "",重复叠加 1 次是 "abc",重复叠加 2 次是 "abcabc"

 

思路

先确定上界和下界

下界:a的复制长度大于等于b的长度,才有可能匹配

上界主串由a复制过来且从主串中找到子串b,因此子串的起始位置不会超过a的长度

class Solution {

    public int repeatedStringMatch(String a, String b) {

        StringBuilder sb = new StringBuilder();

        int num = 0;

 

        while(sb.length() < b.length()){

            sb.append(a);

            num++;

        }

 

        sb.append(a);

 

        // 用于检索 StringBuild中是否存在子串 出现子串的首次位置

        int index = sb.indexOf(b);

 

        if(index == -1){

            return -1;

        }

 

        return index + b.length() > a.length() * num ? num+1:num;

 

    }

}

 

KMP 算法是一个快速查找匹配串的算法,它的作用其实就是本题问题:如何快速在「原字符串」中找到「匹配字符串」的下标。

https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247486317&idx=1&sn=9c2ff2fa5db427133cce9c875064e7a4&chksm=fd9ca072caeb29642bf1f5c151e4d5aaff4dc10ba408b23222ea1672cfc41204a584fede5c05&token=1782709324&lang=zh_CN#rd

 

 

排列硬币

你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。

 

给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。

 

 

思路一

等差序列

 

二分查找计算 n 枚硬币

 

 

 

思路二

 

思路三暴力

class Solution {

    public int arrangeCoins(int n) {

        int i = 0;

        long total = 0;

        while(total<n){

            i++;

            total += i;

        }

        if (total == n){

            return i;

        }

        else{

            return i-1;

        }

    }

}

 

posted @ 2024-02-18 22:08  wangssd  阅读(17)  评论(0编辑  收藏  举报