写在前面

前面研究OS的经历实在是令人心力憔悴。。所以换个新鲜的,把自己的刷题感悟整理一番。刷了有些题了,就先拿最近几天hard题打头阵吧。首先说的是(065)Valid Number这个题,其实一眼看起来很简单,不就是for/while/if/else吗?那么你可能不知道这道题其实有一个更加简(bian)洁(tai)的方法,听我慢慢道来。

题目要求

Validate if a given string is numeric.

Some examples: "0" => true " 0.1 " => true "abc" => false "1 a" => false "2e10" => true

Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.

常规解法

Java for LeetCode 065 Valid Number

public boolean isNumber(String s) {
s = s.trim();
String[] splitArr = s.split("e");
if (s.length() == 0 || s.charAt(0) == 'e'
|| s.charAt(s.length() - 1) == 'e' || splitArr.length > 2)
return false;
for (int k = 0; k < splitArr.length; k++) {
String str = splitArr[k];
boolean isDecimal = false;
if (str.charAt(0) == '-' || str.charAt(0) == '+')
str = str.substring(1);
if (str.length() == 0)
return false;
for (int i = 0; i < str.length(); i++) {
if ('0' <= str.charAt(i) && str.charAt(i) <= '9')
continue;
else if (str.charAt(i) == '.' && !isDecimal) {
if (k == 0 && str.length() > 1)
isDecimal = true;
else
return false;
} else
return false;
}
}
return true;
}

对常规解法的评价

只要会点C++,那么常规解法就不在话下,其实就是手动实现一遍itoa而已。

常规解法的优点是:门槛低/常人写得出/容易修改,也就是定制性好/扩展性差。

同时,它的缺点是:一旦验证逻辑变复杂,那就gg了。比如我想把复数也算进去啊,那又得改那堆杂七杂八的代码,令人感觉不会再爱了。

正则表达式

正则表达式30分钟入门教程总结得比较好。简单来说,正则表达式(regex)可以表示一个特定的词法(编译原理之词法分析、语法分析、语义分析 - nic_r的专栏 - 博客频道 - CSDN.NET),如整数、实数、复数、邮箱地址、电话号码等。regex除了有匹配的功能之外,它还带有替换/解析功能,这样,能够满足涉及字符串操作的大多数需求。

比如,匹配方面,涉及用户名匹配、邮箱地址的匹配等,如果这时你用常规解法就太臃肿、太麻烦了。替换/解析方面,如解析HTML/XML/JSON等,比如便捷。

那么正则表达式与常规解法有什么不同呢?

刚才提到,常规解法虽然容易修改,但它的扩展性不足,我想更改一点需求,就要大刀阔斧改代码,令人不会再爱。那么如何解决这个扩展性的问题呢?那就需要将算法给抽象出来。

如果单用if/else/while/for做一个邮箱匹配,这时又需要做一个数字匹配功能,那么这两种代码是八竿子打不着的,根本没法子复用代码啊,怎么办呢?

其实稍微用脑子想一想——你写的爬虫程序和他写的游戏程序也是风马牛不相及吧?但是编译器将它们翻译成汇编语言后,是不是又有共同点了?比如都有Jump跳转啊,有mov啊,相似度瞬间提高。这里面的原理是什么呢?原来杂七杂八的代码间,通过编译器的翻译,竟然变成了两份差不多的汇编代码(指用的指令大体相似)。那么方法是翻译吗?

也就是说,原始的两种内容不同的代码,可能甲有着C++的高级特性,乙又是C写的,它们翻译成汇编后,用到的指令有99%都是相同的。反过来,如果我以汇编语言为标准,来表示甲和乙,那么这时候两者的代码有99%是相似的。这时,我们发现了可重用性!

回过头来,想一想,假如有一种语言a可以表达数字、邮箱地址,那我们就不需要再写不同的C/C++代码了,即:有一种机制将你的语言a的表达式翻译成对应的代码,运行这个代码,可以完成匹配工作。这不就是编译器干的事么?

啰嗦了那么多,其实意思就是:想要增加两种功能不同的代码之间的相似程度,必须从代码中的相同点/不同点抽象出一种崭新的语言,用这种崭新的语言可以以统一的语法形式来表达这两份代码。

而正则表达式,正是一种崭新的语言。涉及正则表达式的语法、使用、解析,及NFA、DFA等知识这里不再赘述,请参阅专业书籍或是一些博客。

大致步骤是:

  1. 输入正则表达式串pat

  2. 根据手写的LL1解析pat,生成AST
  3. 根据AST构建NFA,添加Epsilon边
  4. 从NFA转换为DFA,合并状态,确定终态
  5. DFA最小化,生成状态转移矩阵
  6. 根据状态转移矩阵进行匹配

轮子的用武之地

还好自己的https://github.com/bajdcc/jMinilang中有生成DFA的代码。

正则匹配部分在priv.bajdcc.util.lexer.test.TestRegex,直接运行它,然后输入上述正则表达式,那么具体信息就出来了。

详细信息(程序自动生成):

#### 正则表达式语法树 ####
序列 {
循环{0,-1} {
字符 [\u0020,' ']
}
循环{0,1} {
字符 [\u002b,'+'],[\u002d,'-']
}
分支 {
序列 {
循环{0,-1} {
字符 [\u0030,'0']-[\u0039,'9']
}
循环{0,1} {
字符 [\u002e,'.']
}
循环{1,-1} {
字符 [\u0030,'0']-[\u0039,'9']
}
}
序列 {
循环{1,-1} {
字符 [\u0030,'0']-[\u0039,'9']
}
循环{0,1} {
字符 [\u002e,'.']
}
循环{0,-1} {
字符 [\u0030,'0']-[\u0039,'9']
}
}
}
循环{0,1} {
序列 {
字符 [\u0065,'e']
循环{0,1} {
字符 [\u002b,'+'],[\u002d,'-']
}
循环{1,-1} {
字符 [\u0030,'0']-[\u0039,'9']
}
}
}
循环{0,-1} {
字符 [\u0020,' ']
}
} #### 状态集合 ####
[\u0020,' ']
[\u002b,'+']
[\u002d,'-']
[\u002e,'.']
[\u0030,'0']-[\u0039,'9']
[\u0065,'e']
#### 最小化 ####
状态[0] => 0,
边 => [1]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
边 => [0]
类型 => 字符区间 [\u0020,' ']
边 => [2]
类型 => 字符区间 [\u002e,'.']
边 => [3]
类型 => 字符区间 [\u002b,'+']
边 => [3]
类型 => 字符区间 [\u002d,'-']
状态[1][结束] => 3,4,6,
边 => [4]
类型 => 字符区间 [\u002e,'.']
边 => [5]
类型 => 字符区间 [\u0065,'e']
边 => [6]
类型 => 字符区间 [\u0020,' ']
边 => [1]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
状态[2] => 5,
边 => [7]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
状态[3] => 2,
边 => [2]
类型 => 字符区间 [\u002e,'.']
边 => [1]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
状态[4][结束] => 5,8,
边 => [5]
类型 => 字符区间 [\u0065,'e']
边 => [6]
类型 => 字符区间 [\u0020,' ']
边 => [4]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
状态[5] => 10,
边 => [8]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
边 => [9]
类型 => 字符区间 [\u002b,'+']
边 => [9]
类型 => 字符区间 [\u002d,'-']
状态[6][结束] => 11,
边 => [6]
类型 => 字符区间 [\u0020,' ']
状态[7][结束] => 6,
边 => [5]
类型 => 字符区间 [\u0065,'e']
边 => [7]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
边 => [6]
类型 => 字符区间 [\u0020,' ']
状态[8][结束] => 14,
边 => [6]
类型 => 字符区间 [\u0020,' ']
边 => [8]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9']
状态[9] => 13,
边 => [8]
类型 => 字符区间 [\u0030,'0']-[\u0039,'9'] #### 状态转移矩阵 ####
0 3 3 2 1 -1
6 -1 -1 4 1 5
-1 -1 -1 -1 7 -1
-1 -1 -1 2 1 -1
6 -1 -1 -1 4 5
-1 9 9 -1 8 -1
6 -1 -1 -1 -1 -1
6 -1 -1 -1 7 5
6 -1 -1 -1 8 -1
-1 -1 -1 -1 8 -1

解决方案

class Solution {
inline int getCharMap(const char& c) {
switch (c) {
case ' ':
return 0;
case '+':
return 1;
case '-':
return 2;
case '.':
return 3;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return 4;
case 'e':
return 5;
}
return -1;
} public:
bool isNumber(string s) {
using t = int(*)[6];
int mm[] = {
0 ,3 ,3 ,2 ,1 ,-1,
6 ,-1 ,-1 ,4 ,1 ,5,
-1 ,-1 ,-1 ,-1 ,7 ,-1,
-1 ,-1 ,-1 ,2 ,1 ,-1,
6 ,-1 ,-1 ,-1 ,4 ,5,
-1 ,9 ,9 ,-1 ,8 ,-1,
6 ,-1 ,-1 ,-1 ,-1 ,-1,
6 ,-1 ,-1 ,-1 ,7 ,5,
6 ,-1 ,-1 ,-1 ,8 ,-1,
-1 ,-1 ,-1 ,-1 ,8 ,-1,
};
auto m = (t)mm;
bool final[] = {0, 1, 0, 0, 1, 0, 1, 1, 1, 0};
int status = 0;
auto c = s.c_str();
for (;;) {
auto local = *c++;
int charClass = getCharMap(local);
int refer = -1;
if (charClass != -1) {
refer = m[status][charClass];
}
if (refer == -1) {
return local == 0 && final[status];
} else {
status = refer;
}
}
}
};

https://zhuanlan.zhihu.com/p/25879478备份。

【LeetCode】065-验证数字的更多相关文章

  1. [LeetCode] Valid Number 验证数字

    Validate if a given string is numeric. Some examples:"0" => true" 0.1 " => ...

  2. 前端与算法 leetcode 125. 验证回文串

    目录 # 前端与算法 leetcode 125. 验证回文串 题目描述 概要 提示 解析 解法一:api侠 解法二:双指针 算法 传入测试用例的运行结果 执行结果 GitHub仓库 查看更多 # 前端 ...

  3. JS正则表达式验证数字

    <script type="text/javascript"> function validate(){ var reg = new RegExp("^[0- ...

  4. JS正则表达式验证数字(很全)

    1.<script type="text/javascript"> 2.     function validate(){ 3.       var reg = new ...

  5. JS正则表达式验证数字非常全

    <script type="text/javascript"> function validate(){ var reg = new RegExp("^[0- ...

  6. JavaScript 【正则表达式验证数字代码】

    可以看到 Ajax 请求多了个 x-requested-with ,可以利用它,request.getHeader("x-requested-with"); 为 null,则为传统 ...

  7. 正则表达式验证数字、汉字、电话号码,email,整数,浮点数

    验证数字的正则表达式集 验证数字:^[0-9]*$验证n位的数字:^\d{n}$验证至少n位数字:^\d{n,}$验证m-n位的数字:^\d{m,n}$验证零和非零开头的数字:^(0|[1-9][0- ...

  8. C# 验证数字

    /// <summary> /// 验证数字 /// </summary> /// <param name="number">数字内容</ ...

  9. Atitit  验证 数字验证 非空验证的最佳算法  h5

    Atitit  验证 数字验证 非空验证的最佳算法  h5 <td><select class="searchBox-select"   style=" ...

  10. 黄聪:JS正则表达式验证数字

    <script type="text/JavaScript">     function validate(){       var reg = new RegExp( ...

随机推荐

  1. ASP.NET 5 Beta5来了(翻译)

    在6月30日微软发布了ASP.NET 5 Beta5,我们可以从http://nuget.org上获取Beta5 的packages. 随着VS2015RC发布的ASP.NET 5的版本号是Beta4 ...

  2. 策略模式Strategy——回家乘什么车?

    1.问题与模式 时间:2014年6月       学校:廊坊师范        家:石家庄       人物:学生 又快到期末考试了,回家的节奏也奔上日程.无聊之余就想想这次回家的事儿.对我来说回家主 ...

  3. 解决 PowerDesigner 错误 The generation has been cancelled because errors have been found by the check model.

    在通过概念数据模型生成为物理数据模型时出现错误“The generation has been cancelled because errors have been found by the chec ...

  4. Kotlin(2): 优雅地扩展类的方法和属性

    欢迎Follow我的GitHub, 关注我的CSDN. 个人博客: http://www.wangchenlong.org/, 最新内容. Kotlin由JetBrains公司推出, 是兼容Java的 ...

  5. MySQL服务器安装完之后如何调节性能

    原文作者: Peter Zaitsev原文来源: http://www.mysqlperformanceblog.com/2006/09/29/what-to-tune-in-mysql-server ...

  6. TensorFlow 入门 上(自用)

    下文会出现的一些知识点:TensorFlow的计算模型.数据模型.运行模型,TensorFlow的工作原理. 两个重要概念——Tensor和Flow: Tensor是张量,在TensorFlow中可以 ...

  7. activeMQ消费消息时网络流量异常大的问题

    http://www.cnblogs.com/baibaluo/archive/2012/12/24/2748468.html#2590289 公司有一个应用,多个线程从activeMQ中取消息,随着 ...

  8. uva10401Injured Queen Problem(递推)

    题目:uva10401Injured Queen Problem(递推) 题目大意:依然是在棋盘上放皇后的问题,这些皇后是受伤的皇后,攻击范围缩小了.攻击范围在图中用阴影表示(题目).然后给出棋盘的现 ...

  9. 查看正在执行的sql语句

    ;WITH t AS( SELECT [Spid] = session_Id, ecid, [Database] = DB_NAME(sp.dbid), [User] = nt_username, [ ...

  10. Java同步机制总结--synchronized

    不久前用到了同步,现在回过头来对JAVA中的同步做个总结,以对前段时间工作的总结和自我技术的条理话.JAVA中synchronized关键字能够 作为函数的修饰符,也可作为函数内的语句,也就是平时说的 ...