您的位置:首页 > 其它

LeetCode-Easy1.2:罗马数字转整数、最长公共前缀、有效括号

2020-02-02 08:35 671 查看

罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符
数值 I=1,V=5,X=10,L=50,C=100,D=500,M=1000
例如, 罗马数字 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 的范围内。

示例 1:

输入: "III"
输出: 3
示例 2:

输入: "IV"
输出: 4
示例 3:

输入: "IX"
输出: 9
示例 4:

输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:

输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

大体分析一下,除了题干给出的那六种情况以外,都是可以根据字母顺序直接映射成对应数值相加。然而,在遇到小数在前,大数在后(IV),则不能直接根据字母顺序映射数值相加,因此此题的难点在于如何处理小数在前,大数在后。

解法:整体对待or区别对待

不管是哪种解法,程序都需要识别这种“小数在前,大数在后”的特殊情况。整体对待不过就是将两个字符直接映射为规定的特殊值。
说的再简单明了一点,就是在I, V, X, L,C,D 和 M基础之上,再增加IV,IX,XL,XC,CD,CM六种,字母-数值映射。只不过这个字母数值映射要求字母是两位,而不是一位。

class Solution {
public:
int romanToInt(string s) {
map<string, int> m = {{"I", 1}, {"IV", 4}, {"IX", 9}, {"V", 5}, {"X", 10}, {"XL", 40}, {"XC", 90}, {"L", 50}, {"C", 100}, {"CD", 400}, {"CM", 900}, {"D", 500}, {"M", 1000}};
int sum = 0;
for(int i=0; i<s.size();){
if(m[s.substr(i,1)]<m[s.substr(i+1,1)]){
sum+=m[s.substr(i,2)];
i+=2;
}
else{
sum+=m[s.substr(i,1)];
++i;
}
}
return sum;
}
};

其实这种解决方法并不可取。首先是哈希表是按照map<string,int>进行映射的,这就要求在处理string的每个元素时,还要在做一次char->string,或者直接取子串,因此性能非常 emmmmmm…
所以有了下面这种解法,避免取子串操作,但是不使用哈希表的劣势也异常明显…

class Solution {
public:
int romanToInt(string s) {
int sum = 0;
for (int i = 0;i<s.size();++i){
switch(s[i]){
case 'I':
if(s[i+1]=='V'){
sum+=4;
++i;
}else if(s[i+1]=='X'){
sum+=9;
++i;
}else{
sum+=1;
}
break;
case 'V':
sum+=5;
break;
case 'X':
if(s[i+1]=='L'){
sum+=40;
++i;
}else if(s[i+1]=='C'){
sum+=90;
++i;
}else{
sum+=10;
}
break;
case 'L':
sum+=50;
break;
case 'C':
if(s[i+1]=='D'){
sum+=400;
++i;
}else if(s[i+1]=='M'){
sum+=900;
++i;
}else{
sum+=100;
}
break;
case 'D':
sum+=500;
break;
case 'M':
sum+=1000;
break;
}
}
return sum;
}
};

结果就是需要用大量switch-case来区别情况,虽然时间复杂度很低,但情况也需要逐一点明。
然而,区别对待的算法思路比较新颖,在识别出六种特殊情况之一以后,对前一个数作减法。例如IV,前一个1视为-1即可。这样在按照字母顺序处理便没有任何问题。

class Solution {
public:
int romanToInt(string s) {
map<char,int>mp={{'I', 1},{'V', 5}, {'X', 10},{'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}};
int sum = 0;
for(int i=0;i<s.size();++i){
if(mp[s[i]]<mp[s[i+1]]){
sum-=mp[s[i]];
}else{
sum+=mp[s[i]];
}
}
return sum;
}
}

经过这么一番比较,使用哈希表的优势越来越明显。

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。
说明:所有输入只包含小写字母 a-z

示例 1:

输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:

输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。

取公共前缀比较常用的方法是水平扫描法,所以此题不存在难度上的争议…

解决:水平扫描法

class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.size()==0)
return "";
string a = strs[0];
string b = "";
int flag = 0;
for(int i = 0;i<a.size()&&flag==0;++i){
for(int j = 1;j<strs.size()&&flag==0;++j){
if(a[i]!=strs[j][i]||i>strs[j].size())
flag=1;
}
if(flag==1)
break;
b+=a[i];
}
return b;
}
};

取字符串数组的首字符串作为标准,其他字符串与其比较。逐位开始,拿首字符串第i位与其他串的第i位进行比较。如果所有字符串对应位通过,则加入字符串b。

有效括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true
示例 2:

输入: "()[]{}"
输出: true
示例 3:

输入: "(]"
输出: false
示例 4:

输入: "([)]"
输出: false
示例 5:

输入: "{[]}"
输出: true

如果有同学之前学的时候有学习过四则运算法的相关程序,那么这个题确实没有难度了,拿栈来处理这种优美的对称比较问题再合适不过。

解决:Stack+HashTable

先分析一下问题,由于括号是成对出现的,而且满足一定对称关系。,当输入一个反括号的时候,就必须有对之对应的左括号。
所以,我们考虑,在遍历的时候,一旦遇到左括号就入栈;反之,一旦遇到右括号,就判断是否栈顶元素匹配,如果二者不匹配,说明错误,如果二者匹配,则弹栈。这就是“栈”在这个题目中的作用。

关于匹配,还需要多说两句,先举个例子,如果遇到了’]’,那么就要弹出栈顶元素,可是你还要再判断一下栈顶是不是’[’。然而这只是一种情况,实际上,有三种不同的括号,也就是说你需要先判断是哪种括号以后,在判断匹配问题。显然,这种if-else或者switch-case的思维定式让许多同学走了弯路。

其实,我们设计程序之前,已经知道匹配关系是怎样了,完全可以做HashTable的映射:先通过HashTable找到这种匹配关系,在判断栈顶元素是否满足这种匹配关系即可。

class Solution {
public:
bool isValid(string s) {
map<char,char> mp={{')','('},{'}','{'},{']','['}};
if (s.size()==0)
return true;
if (s.size()==1)
return false;
stack<char> stk;
for(int i=0;i<s.size();++i){
if(s[i]=='('||s[i]=='{'||s[i]=='[')
stk.push(s[i]);
else{
if(stk.empty()||stk.top()!=mp[s[i]])
return false;
stk.pop();
}
}
if(stk.empty())
return true;
else
return false;
}

};

HashTable为我们提供了许许多多的便利,不单单是搜索难度降低,更是这种键值对的形式非常有利于我们实现任何想要的一对一映射。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
标准的搬运工 发布了7 篇原创文章 · 获赞 5 · 访问量 1520 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: