5月23日 每日leetcode三题(13/6/76)

前言

今天刷的是13.罗马数字转整数、6.Z 字形变换、76.最小覆盖子串。

13.罗马数字转整数(容易):
题目描述:

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符数值
I1
V5
X10
L50
C100
D500
M1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做  XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/roman-to-integer

题解:

这道题虽然归在简单里面,不过我觉得也是比较难的。不过发现规律之后就很好做了,里面比较特殊的情况是IV、IX、XL、XC、CD、CM这六个,其实就是把一个较小的罗马数字放在了较大的罗马数字之前。我们可以在遍历时判断相邻字符,在遇到这种情况时减去上一个字符代表的数字,其余时候只要累加。

Code
// 第一次AC
var romanToInt = function(s) {
    let sum=0;
    let preNum=getValue(s[0]);
    for(let i=1;i<s.length;i++){
        let num=getValue(s[i]);
        if(preNum<num){
            sum-=preNum;
        }else{
            sum+=preNum;
        }
        preNum=num;
    }
    sum+=preNum;
    return sum;
};
function getValue(char){
    switch(char){
        case 'I': return 1;
        case 'V': return 5;
        case 'X': return 10;
        case 'L': return 50;
        case 'C': return 100;
        case 'D': return 500;
        case 'M': return 1000;
        default: return 0;
    }
}
// console.log(romanToInt("MCMXCIV"))
6.Z 字形变换(中等):
题目描述:

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
详细题意见leetcode链接。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zigzag-conversion

题解:

这道题是一道思维题,我的做法是用字符串数组存下每一行从左到右的读取结果,最后进行拼接。而遍历字符串时我们也只需要按顺序,然后判断当前字符位于哪一行,塞到当前行的字符串的末尾即可。因此时间复杂度为O(n),n是输入字符串长度。

Code
// 第一次AC
var convert = function(s, numRows) {
    if(numRows==1)
        return s;
    const len=Math.min(s.length,numRows);
    const rows=[];
    for(let i=0;i<len;i++)
        rows[i]="";
    let loc=0;
    direction=false;
    for(const c of s){
        rows[loc]+=c;
        if(loc==0||loc==numRows-1)
            direction=!direction;
        loc+=direction?1:-1;
    }
    let res="";
    rows.forEach(item=>{
        res+=item;
    })
    return res;
};
76.最小覆盖子串(困难):
题目描述:

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。

示例:

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”

说明:

如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring

题解:

这道题试过穷举(超时)之后,观摩了讨论区大佬的滑动窗口解法,豁然开朗。
滑动窗口解法使用左右两个指针来控制子串的起点和终点,在搜索的过程中我们可以通过哈希表存储缺失的字符,在匹配的情况下减去对应的哈希表项。
同时,通过缺失的种类数来控制左指针的移动,当缺失种类数为0,即不缺失要求的字符时,我们认为当前的子串已经达到了一个局部的优解,为了验证它是否是最优的,我们需要缩小窗口,即移动左指针,寻找有没有更短的子串。在移动左指针的过程中,假如失去了已经匹配好的字符,就令缺失字符的种类数+1,使得程序停止移动左指针,开始移动右指针达到符合条件的状态。循环反复,直至右指针最终到达字符串的末端。
在这一过程中,我们可以发现,字符串中的每个字符至多会被指针指向并判断两次,时间复杂度远比罗列所有的子串的时间复杂度要低。

Code
// 滑动窗口
var minWindow = function(s, t) {
    // if(s.length==0||t.length==0||t.length>s.length) return "";
    let minLen=Infinity,resL;
    let map={};
    let missingType=0;//当前缺失的字符种类
    for(const char of t){
        if(!map[char]){
            missingType++;
            map[char]=1;
        }else{
            map[char]++;
        }
    }
    let left=0,right=0;
    for(;right<s.length;right++){
        let rightChar=s[right];
        if(map[rightChar]!==undefined)//当前字符是T包含的字符
            map[rightChar]--;
        if(map[rightChar]===0)
            missingType--;
        while(missingType===0){
            if(right-left+1<minLen){
                minLen=right-left+1;
                resL=left;
            }
            let leftChar=s[left]
            if(map[leftChar]!==undefined)
                map[leftChar]++;
            if(map[leftChar]>0) 
                missingType++;
            left++;
        }
    }
    return s.substring(resL,resL+minLen);
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值