Leetcode初体验生存向——Day1

1. Two Sum (Easy)

Question:

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

Solution:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2]; // 存放结果的数组是一个大小为2的一维数组
        Map<Integer, Integer> cache = new HashMap<Integer, Integer>(); // 存储用的map容器cache
        for(int i = 0; i < nums.length; i++) { // 遍历数组
            if(cache.containsKey(nums[i])) { // 如果cache容器的key中有数组中数字2的这个值
                result[0] = cache.get(nums[i]); // 取出cache容器中对应数字2的value(即数字1的下标)
                result[1] = i; // 将数字2的下标放到结果数组中
                return result; // 打印结果
            }
            cache.put(target-nums[i], i); // 如果容器中没有该值,放入数字2(目标减去数字1),及数字1的下标,再次遍历
        }
        return null; // 遍历结束都没有符合条件的值,返回空
    }
}

链接:https://leetcode-cn.com/problems/two-sum

2. Add Two Numbers(Middle)

Question:

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

Solution:

/**
 * Definition for singly-linked list. 单向链表
 * public class ListNode { 思路是小学竖式计算
 *     int val; 结点的值(数据域)
 *     ListNode next; 下一个结点
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) { // l1链表1,l2链表2
        ListNode dummyHead = new ListNode(0); // 先给新链表设置一个哑结点,可以避免入口参数为空的异常问题,这里设为0
        ListNode p = l1, q = l2; // p和q指针分别指向两个链表的当前结点
        ListNode current = dummyHead; // 让新链表的当前结点指针指向哑结点

        int carry = 0; // 进位

        while(p != null || q != null){ // 只要有一个链表中还有值(避免两个链表长度不相等)
            int x = (p != null) ? p.val : 0; // 如果两个表长度不等,短的那个到头了,自动补零
            int y = (q != null) ? q.val : 0;
            int sum = x + y + carry; // 当前值为两数相加加上当前的进位
            carry = sum/10; // 更新下一个进位
            current.next = new ListNode(sum%10); // 十进制直接求余得到当前位数
            current = current.next; // 指针后移
            if (p != null) p = p.next; // 链表中还有内容,指针后移
            if (q != null) q = q.next;
        }
        if (carry > 0) {
            current.next = new ListNode(carry); //如果两个表加完了还要进位,即有溢出,新建一个结点,赋值为进位制
        }
        return dummyHead.next; // 加好的链表从dummyhead的下一位开始读,因为dummyhead指的是表头前一位
    }
}

链接:https://leetcode-cn.com/problems/add-two-numbers

3. Longest Substring Without Repeating Characters(Middle)

Question:

Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.

Example 2:

Input: “bbbbb”
Output: 1
Explanation: The answer is “b”, with the length of 1.

Example 3:

Input: “pwwkew”
Output: 3
Explanation: The answer is “wke”, with the length of 3.
Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

Solution:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(); // 给出的字符串长度
        int ans = 0; // 初始化最长子串长度
        Map<Character, Integer> map = new HashMap<Character, Integer>(); // 初始化容器map<当前位的char, 结束位指针>
        for(int start = 0, end = 0 ; end < n ; end++) { // 起始位和当前位指针(end+1为结束位指针)
            char now = s.charAt(end); // 当前位字符
            if(map.containsKey(now)) { // 如果map中存放的char出现重复了
                start = Math.max(map.get(now), start); // 将起始位挪到当前位(重复的那个字符)的下一位开始判断新的字符串
            }
            ans = Math.max(end+1-start, ans); // 记录当前最长长度
            map.put(s.charAt(end), end+1); // 将当前字符以及结束位(可能是新的起始位)放入容器
        }
        return ans; // 滑动窗口循环结束,返回最长字串长度
    }
}

链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters

4. Median of Two Sorted Arrays(Hard)

Question:

Given two sorted arrays nums1 and nums2 of size m and n respectively.

Return the median of the two sorted arrays.

Follow up: The overall run time complexity should be O(log (m+n)).

Example 1:

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

Example 3:

Input: nums1 = [0,0], nums2 = [0,0]
Output: 0.00000

Example 4:

Input: nums1 = [], nums2 = [1]
Output: 1.00000

Example 5:

Input: nums1 = [2], nums2 = []
Output: 2.00000

Constraints:

  • nums1,length == m
  • nums2,length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000

Solution:

/**
* 思路1是硬解法,将两个数组合并为一个,重新排序,求中位数,但run time complexity为O(m+n)
* 思路2是二分法(一般看到时间复杂度是log就应当是二分法):
    假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,
    如果当前位置arr[k]值等于key,则查找成功;
    若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1];
    若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high],
    直到找到为止,时间复杂度:O(log(n))。
*   因为两个数组均是升序排列,因此可以不合并数组:
*   假如数组有[1349],[123456789]
*   共14位数,则中位数应当在k=14/2=7
*   取数组1的第k/2=3位数,与数组2的第k/2=3位数对比,4>3
*   去掉数组2的前3位数,新的数组为[1349],[456789]
*   因为去掉了3位数,那么进行新的排序时,应当是k=k-k/2=4
*   取数组1的第k/2=2位数,与数组2的第k/2=2位数对比,3<4
*   去掉数组1的前2位数,新的数组为[49],[456789]
*   因为去掉了2位数,那么进行新的排序时,应当是k=k-k/2=2
*   取数组1的第k/2=1位数,与数组2的第k/2=1位数对比,4=4
*   随意去掉一个,新的数组为[9],[456789]
*   因为去掉了1位数,那么进行新的排序时,应当是k=((((10/2)-2)-1)-1)/2=0
*   结束,中位数为(4+5)/2=4.5
*/
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) { // 题目要求返回double类型
        int length1 = nums1.length; // 数组1的长度
        int length2 = nums2.length; // 数组2的长度
        int lengthSum = length1 + length2; // 两个数组加在一起的长度
        if (lengthSum % 2 == 1) { // 数组长度为奇数,中位数只有一个
            int midIndex = lengthSum / 2; // 中位数指针
             // k值应为指针(数组下标)+1,如5个元素,中位数指针指在012第2位,第3个数上
            double mid = getK(nums1, nums2, midIndex + 1);
            return mid;
        } else { // 数组长度为偶数,中位数为中间两数的平均值
            int midIndex1 = lengthSum / 2 - 1 , midIndex2 = lengthSum / 2;
            double mid = (getK(nums1, nums2, midIndex1 + 1) + getK(nums1, nums2, midIndex2 + 1)) / 2.0;
            return mid;
        }
    }

    public int getK(int[] nums1, int[] nums2, int k) { // k:中位数为当前序列的第k个元素,即数组中第k-1位数
        int length1 = nums1.length; // 数组1的长度
        int length2 = nums2.length; // 数组2的长度
        int startIndex1 = 0; // 数组1的指针
        int startIndex2 = 0; // 数组2的指针

        while (true) {
            // 边界情况
            if (startIndex1 == length1) { // 数组1没有元素了
                return nums2[startIndex2 + k - 1]; // 用数组2的指针直接加上中位数指针(k-1)就可以得到中位数位置
            }
            if (startIndex2 == length2) { // 数组2没有元素了
                return nums1[startIndex1 + k - 1]; // 用数组1的指针直接加上中位数指针(k-1)就可以得到中位数位置
            }
            //如果最后中位数就是序列中第一个数了,即k=1(数组的第0位数),结束循环
            if (k == 1) {
                return Math.min(nums1[startIndex1], nums2[startIndex2]); // 返回小的那个
            }

            // 除去上述特殊情况的正常情况
            int half = k / 2;
            // 设置新的指针指在half长度的数组的末尾处,考虑到half可能超出数组长度,这里用min加一个限制
            int endIndex1 = Math.min(startIndex1 + half, length1) - 1;
            int endIndex2 = Math.min(startIndex2 + half, length2) - 1;
            int now1 = nums1[endIndex1], now2 = nums2[endIndex2]; // 当前两个指针内数组最后一位
            // 如果数组1的数比数组2的小,说明还没达到中位数标准,去掉数组1前面的这些数,继续往后找(如果相等,也去掉1的)
            if (now1 <= now2) {
                k -= (endIndex1 - startIndex1 + 1);
                startIndex1 = endIndex1 + 1; // 挪动指针,相当于删除数组前面的元素
            } else { // 如果数组2的数比数组1的小,说明还没达到中位数标准,去掉数组2前面的这些数,继续往后找
                k -= (endIndex2 - startIndex2 + 1);
                startIndex2 = endIndex2 + 1; // 挪动指针,相当于删除数组前面的元素
            }
        }
    }
}

链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

5. Longest Palindromic Substring(Middle)

Question:

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example1:

Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.

Example2:

Input: “cbbd”
Output: “bb”

Solution:

/**
* 回文是从左读与从右读完全相等的字符串
* 此处使用中心扩散法:
* 遍历所有字符设置为中心点,从中心点开始像两边扩散;
* 如果左右指针指字符相等,继续扩散,不相等则返回当前字符串。
* 不设置最后一个点为回文中心,因为最后一个点无法继续扩散,没必要
*/
class Solution {
    public String longestPalindrome(String s) {
        int length = s.length();
        if (length < 2) { // 如果只有一个字符或者没有字符,直接返回
            return s;
        }
        int maxLen = 1;
        String res = s.substring(0, 1); // 如果全不是回文,可以返回第一个字符
        for (int i = 0 ; i < length-1 ; i++) {
            String oddStr = centerSpread(s, i, i); // 回文串长度为奇数,以i为中心向两边扩散
            String evenStr = centerSpread(s, i, i+1); // 回文串长度为偶数,以i与i+1为中心向两边扩散
            String maxStr = oddStr.length() > evenStr.length() ? oddStr : evenStr; // 判断一下奇数回文与偶数回文最长的那个
            if (maxStr.length() > maxLen) {
                maxLen = maxStr.length();
                res = maxStr; 
            }
        }
        return res;
    }

    public String centerSpread(String s, int left, int right) {
        int length = s.length();
        int i = left; // 左指针
        int j = right; // 右指针
        while (i >= 0 && j < length) { // 在字符串范围内循环
            if(s.charAt(i) == s.charAt(j)){ // 满足回文条件
                i--; // 左指针左移
                j++; // 右指针右移
            } else {
                break; // 不满足回文条件就退出循环
            }
        }
        // 截取字符串,但是因为截取前,i与j已发生变化,因此要给i加回去
        //substring是左闭右开字符串,j不需要变化,截取时会自动变为j-1
        return s.substring(i + 1, j);
    }
}

链接:https://leetcode-cn.com/problems/longest-palindromic-substring

6. ZigZag Conversion(Middle)

Quetion:

The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P       A      H      N
A   P   L   S  I   I  G
Y       I      R

And then read line by line: “PAHNAPLSIIGYIR”

Write the code that will take a string and make this conversion given a number of rows:

string convert(string s, int numRows);

Example 1:

Input: s = “PAYPALISHIRING”, numRows = 3
Output: “PAHNAPLSIIGYIR”

Example 2:

Input: s = “PAYPALISHIRING”, numRows = 4
Output: “PINALSIGYAHRPI”

Solution:

/**
* 通过字符串找规律,发现:
* 第0行:(numRows-1)*2k
* 第i行:(numRows-1)*2k+i 以及 (numRows-1)*2(k+1)-i
* 第numRows-1行:(numRows-1)*2k
*/
class Solution {
    public String convert(String s, int numRows) {

        // 如果只有1行,直接返回就行了
        if (numRows == 1) return s;

        StringBuilder newStr = new StringBuilder(); // 用StringBuilder代替String更好一些(不用疯狂分内存)
        int length = s.length();
        int cycleLength = (numRows - 1) * 2; // 定义一个循环长度,就不用乘k了,在循环时直接加就行

        for (int i = 0 ; i < numRows ; i++) { // 按照z的行数循环
            for (int j = 0 ; j + i < length ; j += cycleLength) { // 循环时加循环长度
                // 这里已经有了其它行(numRows-1)*2k+i位置上的字符(以及第一行和最后一行的字符)
                newStr.append(s.charAt(j + i));
                if (i != 0 && i != numRows - 1 && j + cycleLength - i < length) {
                    // 把除去第一行和最后一行的其它行的(numRows-1)*2(k+1)-i位置上的字符加上
                    newStr.append(s.charAt(j + cycleLength - i));
                }
            }
        }
        return newStr.toString();
    }
}

链接:https://leetcode-cn.com/problems/zigzag-conversion

7. Reverse Integer(Easy)

Question:

Given a 32-bit signed integer, reverse digits of an integer.

Example 1:

Input: 123
Output: 321

Example 2:

Input: -123
Output: -321

Example 3:

Input: 120
Output: 21

Note:

Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

Solution:

/**
* 字符串反转效率低,用的库函数太多,再加上本题有溢出问题
* 因此直接用数学计算就行
* 数值范围是[−2^31,  2^31 − 1],即[-2147483648, 2147483627]
* 恰好是int类整数的取值范围
* 考虑可能是个位以上溢出或者个位溢出,因此规划两个条件
* 将原整数通过求余的方式取出个位,并依次乘10加到结果上,就可以完成反转
*/
class Solution {
    public int reverse(int x) {
        int ans = 0; // 设置结果
        while (x != 0) { // 到x没有位数了停止
            int pop = x % 10; // pop是出栈的意思,从个位开始移除数字
            // 向上溢出,ans是十位及以上数字,所以Integer.MAX_VALUE要除以10取整
            // 判断十位及以上是否溢出,并判断个位是否溢出,此时个位最大可取值是7
            if (ans > Integer.MAX_VALUE / 10 || (ans == Integer.MAX_VALUE / 10 && pop > 7))
                return 0;
            // 向下溢出
            // 判断十位及以上是否溢出,并判断个位是否溢出,此时个位最小可取值是-8
            if (ans < Integer.MIN_VALUE / 10 || (ans == Integer.MIN_VALUE / 10 && pop < -8))
                return 0;
            ans = ans * 10 + pop; // 反转计算
            x = x / 10;
        }
        return ans;
    }
}

作弊法:

class Solution {
	public int reverse(int x) {
		int ans = 0;
		while (x != 0) {
			// 这里仅判断了十位及以上,没有判断个位的原因:
			// 输入int x整数时本身有规定范围是[−2^31,  2^31 − 1]
			// 最高位只能是2或者-2,根本到不了7或-8,因此可以不做判断
			if ((ans * 10) / 10 != ans) {
				ans = 0;
				break;
			}
			ans = ans * 10 + x % 10;
			x = x / 10;
		}
		return ans;
	}
}

链接:https://leetcode-cn.com/problems/reverse-integer

8. String to Integer (atoi) (Middle)

Question:

Implement atoi which converts a string to an integer.

The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value.

The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.

If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed.

If no valid conversion could be performed, a zero value is returned.

Note:

  • Only the space character ’ ’ is considered as whitespace character.
  • Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. If the numerical value is out of the range of representable values, INT_MAX (231 − 1) or INT_MIN (−231) is returned.

Example 1:

Input: “42”
Output: 42

Example 2:

Input: " -42"
Output: -42
Explanation:
The first non-whitespace character is ‘-’, which is the minus sign.
Then take as many numerical digits as possible, which gets 42.

Example 3:

Input: “4193 with words”
Output: 4193
Explanation: Conversion stops at digit ‘3’ as the next character is not a numerical digit.

Example 4:

Input: “words and 987”
Output: 0
Explanation:
The first non-whitespace character is ‘w’, which is not a numerical digit or a +/- sign. Therefore no valid conversion could be performed.

Example 5:

Input: “-91283472332”
Output: -2147483648
Explanation:
The number “-91283472332” is out of the range of a 32-bit signed integer.
Thefore INT_MIN (−231) is returned.

Solution:

/**
* 使用确定有限状态自动机(deterministic finite automaton, DFA)
* https://www.cnblogs.com/dh-dh/p/10245474.html
* (但是感觉这道题不是很需要)
* 确定有限状态自动机首先要有一个确定的输入字符集
* 每一个输入字符c都有一个当前状态s,根据当前字符c转移到下一个状态s'
* 由此往复直到输入字符集输入完毕,得到一个最终状态
* 而其中根据输入字符c改变状态s'可以被写为一个状态表,将其以集合形式输入,就是一个有限状态集合S
* 在代码中根据这个有限状态集合S来判断s'
* (最后用的是暴力解决法)
*/
class Solution {
    public int myAtoi(String str) {
        str = str.trim(); // trim()方法是修建的意思,即去除字符串的首尾空格
        // 字符串长度为0,直接返回为0
        if (str.length() == 0) return 0;
        // 如果开头不是数字或者开头不是+-号,直接返回0
        if (!Character.isDigit(str.charAt(0)) && str.charAt(0) != '+' && str.charAt(0) != '-') return 0;
        // 正式情况
        long ans = 0L; // 初始化最终结果
        boolean neg = str.charAt(0) == '-'; // 判断是否是负数
        int i = !Character.isDigit(str.charAt(0)) ? 1 : 0; // 定义指针,如果第1位不是数字,那就是+-号,从第2位开始转换。否则从第1位开始
        while (i < str.length() && Character.isDigit(str.charAt(i))) {
            ans = ans *10 + (str.charAt(i++) - '0'); //'0'用来将ascll码转换成数字(利用其排序规则)
            if (!neg && ans > Integer.MAX_VALUE) { // 判断正数上限是否溢出
                ans = Integer.MAX_VALUE; // 溢出的话直接返回最大值
                break;
            }
            if (neg && ans > Integer.MAX_VALUE + 1L) { // 判断负数下限是否溢出
                ans = Integer.MAX_VALUE + 1L; // 溢出的话直接返回最小值
                break;
            }
        }
        return neg ? (int) -ans : (int) ans;
    }
}

链接:https://leetcode-cn.com/problems/string-to-integer-atoi

9. Palindrome Number(Easy)

Question:

Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.

Example 1:

Input: 121
Output: true

Example 2:

Input: -121
Output: false
Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.

Example 3:

Input: 10
Output: false
Explanation: Reads 01 from right to left. Therefore it is not a palindrome.

Follow up:

Coud you solve it without converting the integer to a string?

Solution:

/**
* 反转一半数字,与另一半数字做对比,如果相等则是回文数字,不相等不是
* 如果数字长度是偶数,直接对比就行了
* 如果数字长度是奇数,去掉中间的那位再对比
* 负数全部去除
*/
class Solution {
    public boolean isPalindrome(int x) {
        // 去除所有负数
        if (x < 0) {
            return false;
        }
        // 当最后一位数字为0时,将数字反转后第一位也必须是0
        // 只有0可以,其余的去掉
        if (x % 10 == 0 && x != 0) {
            return false;
        }

        int revertedNum = 0;
        while (x > revertedNum) {
            revertedNum = revertedNum * 10 + x % 10;
            x = x / 10;
        }
        return x == revertedNum || x == revertedNum / 10; // 分奇偶数两种情况
    }
}

链接:https://leetcode-cn.com/problems/palindrome-number

10. Regular Expression Matching(Hard)

Question:

Given an input string (s) and a pattern §, implement regular expression matching with support for ‘.’ and ‘*’.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input:
s = “aa”
p = “a”
Output: false
Explanation: “a” does not match the entire string “aa”.

Example 2:

Input:
s = “aa”
p = “a*”
Output: true
Explanation: ‘*’ means zero or more of the preceding element, ‘a’. Therefore, by repeating ‘a’ once, it becomes “aa”.

Example 3:

Input:
s = “ab”
p = ".* "
Output: true
Explanation: ".* " means “zero or more (*) of any character (.)”.

Example 4:

Input:
s = “aab”
p = “c * a * b”
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches “aab”.

Example 5:

Input:
s = “mississippi”
p = “mis * is * p * .”
Output: false

Solution:

/**
* 思路主要参考:https://blog.csdn.net/jfkidear/article/details/90261170
* 其中p中的字符共分为3种情况
* 1. "."为任意字符,可以直接跳过 -> true
* 2. 字符串,s[i]与p[j]对比,相等 -> true
* 3. "*",匹配0个或多个前面的字符(必须与前面字符一起用)
* 设F[m][n]是状态表,使用自底向上求解(非备用录法)
* 好处:从最后一位往前看,只要遇到了"*",就与*前一位一起看
* 判断方法:每次从p中拿出一个j位的字符与s中i位的字符匹配
* (1) "."返回true,s与p的游标向前一位
* (2) 字符串,相等s与p的游标向前一位,不相等返回false
* (3) "*" 用p的*前一位匹配s -> 匹配上了n个,s游标一位位向前移
*                           -> 匹配上了0个,p向前移两位(跳过这个字符,当它为0)
* 全部遍历结束,返回F[0][0],如果最终状态为true,就说明匹配
*/
class Solution {
    public boolean isMatch(String s, String p) {

        int m = s.length();
        int n = p.length();

        boolean[][] F = new boolean[m + 1][n + 1]; // 留出一行一列是为状态初始化做准备

        F[m][n] = true; // 状态初始化(判断两个字符串的最后一个时需要多出来的这个状态)
                           // 如果是备忘录法(自顶向下),初始化状态是F[0][0]=true,则应该空出来0行0列

        // 开始填表
        for (int i = m; i >= 0; i--){
            for (int j = n - 1; j >= 0; j--){
                
                // 看一下当前的s[i]和p[j]匹不匹配,即两个字符相不相等或p[j]是不是"."
                // 匹配 -> true , 不匹配 -> false
                // i < m 是为了排除掉初始化的那行
                boolean match = (i < m && (p.charAt(j) == s.charAt(i) || p.charAt(j) == '.'));

                // 看一下p[j+1]是不是"*"
                // 不是"*"直接返回状态
                // 是"*",走两种情况
                // 这里看p[j+1]是不是"*"可以避免p[0]=="*"的情况
                // j+1 < n 是为了排除掉初始化的那列
                if (j + 1 < n && p.charAt(j+1) == '*'){
                    //是"*" -> 匹配上了0个,p向前移两位(跳过这个字符,当它为0,直接改状态)-> F[i][j] = F[i][j+2]
                    //      -> 匹配上了n个,s游标一位位向前移(移动游标的同时看看匹不匹配) -> F[i][j] = match && F[i+1][j]
                    F[i][j] = F[i][j+2] || match && F[i+1][j];
                } else {
                    // 没有"*"直接返回状态
                    // F[i+1][j+1]是状态方程的前一个状态,必须前一个状态为true才能返回为true
                    // 前一个状态指的是已经进行过判断的子字符串必须匹配
                    // 如果现在的字符匹配,但判断过的子字符串出现了不匹配,最终结果出来仍为false
                    F[i][j] = match && F[i+1][j+1];
                }
            }
        }
        return F[0][0];
    }
}

动态规划参考:https://zhuanlan.zhihu.com/p/31628866
链接:https://leetcode-cn.com/problems/regular-expression-matching

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值