LeetCode -- 最长公共子串、两链表的公共节点、链表相加

291 阅读3分钟

昨天写了草稿,忘了发,今天补上。

今天是记录leetcode刷题的第九天,由于最近工作较多,刷题的速度略有下降。

今天得题目是最长公共子串、两链表的公共节点、链表相加。其中求最长公共子串是一道动态规划的题,但是相对来说还算可以。

最长公共子串

题目要求如下

给定两个字符串str1和str2,输出两个字符串的最长公共子串

题目保证str1和str2的最长公共子串存在且唯一。

正常思路就是暴力匹配法,两个指针i,j分别指向两个字符串的开始位置,此时两指针指向的字符对比,结果无非就是相等或不等,我们假设dp[i][j]表示当前指针指向的位置的字符的最大公共子串的长度

  • 相等时,参照动态规划的思想,此时的值一定跟之前的值有关系,所以略加思考可以得出
dp[i][j]=dp[i1][j1]dp[i][j] = dp[i-1][j-1]
  • 不相等时,那么此时的dp[i][j]就为0
dp[i][j]=0dp[i][j] = 0

有了如上公式可以动手写代码了

代码如下

/**
 * longest common substring
 *
 * @param str1 string字符串 the string
 * @param str2 string字符串 the string
 * @return string字符串
 */
public String getLcs(String str1, String str2) {
    // KMP算法,求较短字符串的next数组
    int len1 = str1.length();
    int len2 = str2.length();
    int[][] dp = new int[len1 + 1][len2 + 1];
    int maxLastIndex = 0, maxLength = 0;
    for (int i = 0; i < len1; i++) {
        for (int j = 0; j < len2; j++) {
            if (str1.charAt(i) == str2.charAt(j)) {
                dp[i + 1][j + 1] = dp[i][j] + 1;
                if (dp[i + 1][j + 1] > maxLength) {
                    maxLength = dp[i + 1][j + 1];
                    maxLastIndex = i;
                }
            } else {
                dp[i + 1][j + 1] = 0;
            }
        }
    }

    return str1.substring(maxLastIndex - maxLength + 1, maxLastIndex + 1);
}

两链表的公共节点

题目要求如下

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

思路如下

其实找链表的公共节点还是很简单的题目,首先我们需要明确,两链表节点相等是建立在其地址相等,不单单是其节点的值相等的。

那么如何找到两链表的公共节点呢?

方法有很多,如下

  • hash
  • 双指针,长链表先行,然后与短链表同行,

代码如下

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    // 这里使用双指针,先求出两个链表的长度,让长的先走
    int size1 = getSize(pHead1);
    int size2 = getSize(pHead2);
    int num = size1 > size2 ? size1 - size2 : size2 - size1;
    if(size1 > size2) {
        while(num-->0){
            pHead1 = pHead1.next;
        }
    }else {
        while(num-->0){
            pHead2 = pHead2.next;
        }
    }
    while(pHead1 != pHead2) {
        pHead1 = pHead1.next;
        pHead2 = pHead2.next;
    }
    return pHead1;
}

public int getSize(ListNode node) {
    int size = 0;

    while (node != null) {
        size++;
        node = node.next;
    }
    return size;
}

链表相加

题目要求如下

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。

数据范围:0≤n,m≤1000000,链表任意值 0≤val≤9

要求:空间复杂度 O(n),时间复杂度 O(n)

思路如下

这个题跟上一题解法类似,难点在于单向链表只能从前往后遍历,但是我们需要从后往前相加,一旦我们遇到类似的问题,我们就可以使用来解决问题,所以这个难点也算不上难点啦。

代码如下

public ListNode addInList(ListNode head1, ListNode head2) {
    // 栈
    Stack<Integer> stack1 = new Stack<>();
    Stack<Integer> stack2 = new Stack<>();
    Stack<Integer> stack3 = new Stack<>();
    while (head1 != null) {
        stack1.push(head1.val);
        head1 = head1.next;
    }
    while (head2 != null) {
        stack2.push(head2.val);
        head2 = head2.next;
    }

    // 进位
    int carry = 0;
    while (!stack1.isEmpty() || !stack2.isEmpty()) {
        // 求和
        int sum = carry;
        if (!stack1.isEmpty()) {
            sum += stack1.pop();
        }
        if (!stack2.isEmpty()) {
            sum += stack2.pop();
        }
        stack3.add(sum % 10);
        carry = sum / 10;
    }
    if(carry > 0){
        stack3.add(carry);
    }
    ListNode newHead = new ListNode(0);
    ListNode tmp = newHead;
    while (!stack3.isEmpty()) {
        tmp.next = new ListNode(stack3.pop());
        tmp = tmp.next;
    }
    return newHead.next;
}

static class ListNode {
    int val;
    ListNode next = null;

    public ListNode(int x) {
        val = x;
    }
}