如何在100万文字的文章中 200ms内 快速提取 替换 上万个关键字
关键点:
关键字 组合 成一棵 hash 树 ( 有属性就直接移动指针到下一个字对象, 没有属性就创建子对象, 再移动指针; 第二次循环在子对象看有没这个属性 )
探测针 标记结束 跳跃 回溯
var treeSearch = {
makeTree: function(strKeys) {
"use strict";
var tblCur = {},
tblRoot,
key,
str_key,
Length,
j,
i
;
tblRoot = tblCur;
/*一次性 把所以的关键字 都放在 hash tree中
abc, abd => { a:{ b: {c: {end:true}, d: {end: true } } } }
abcd, bc => { a:{b:{c:{d:{end:true}}}}, b:{c:{end:true}} }
*/
for ( j = strKeys.length - 1; j >= 0; j -= 1) {
str_key = strKeys[j];
Length = str_key.length;
for ( i = 0; i < Length; i += 1) {
key = str_key.charAt(i);
if (tblCur.hasOwnProperty(key)) { //生成子节点
tblCur = tblCur[key];
} else {
//在当前的对象 创建一个子对象
// 注意 tblRoot 和 tblCur是指向同一个对象 ori, 虽然tblCur移动了
// 但还是指向了对象 ori, 所以利用这个tblCur创建的子对象, 仍然是创建在ori上
// 而 tblRoot 也指向了 ori, 所以每次tblRoot
tblCur[key] = {}
//移动当前指针到 这个新的子对象
tblCur = tblCur[key];
}
}
tblCur.end = true; //最后一个关键字没有分割符
// 回溯, tblCur 指针从新指向根节点, 变成了整个对象
tblCur = tblRoot;
}
return tblRoot;
},
search: function(content, tblRoot) {
"use strict";
var tblCur,
p_safe = 0,
n = content.length,
p_tick,
match, //是否找到匹配
match_key,
match_str,
arrMatch = [], //存储结果
arrLength = 0 //arrMatch的长度索引
;
while (p_safe < n) {
/*
tblRoot { a:{ b: {c: {end:true}, d: {end: true } } }, { } }
n : 10000
*/
tblCur = tblRoot; //回溯整个对象, 从整个tree从新匹配
p_tick = p_safe;
match_str = "";
match = false;
do {
/* UCOKsfKBdaqRYQqSYUYjdvDR
match_key : U
*/
//这个是字符串 别人的东西
match_key = content.charAt(p_tick);
// (向前移动) tblCur指针, 指向下一个 深层子对象
/**
1. tblCur: {a: {b:{}, d:{}} }
2. tblCur: {b:{}, d{} }
*/
// tblCur 是关键字自己的 tree, 匹配到别人的一个字, 就继续前移匹配另一个
tblCur = tblCur[match_key]
if (!tblCur) {
//至少说明 以(a) 开头的字母的词组, 从当前位置 往下连续, 是不可能匹配到了
p_safe += 1;
// 确认安全后, 小不点 p_safe 移动一个位置
break; //直接进入到下一次循环 ----> out
}
match_str += match_key; //保存 [match]
p_tick += 1; // p_safe 小不点, p_tick 小不点的探测器 (向前移动)
if (tblCur.end === true) { //
match = true; //说明这几个木板下有坑
}
} while (true);
if (match === true) { //最大匹配
/**
把找到的 标记起来
**/
arrMatch[arrLength] = { //增强可读性
key: match_str,
begin: p_safe - 1,
end: p_tick
};
arrLength += 1;
p_safe = p_tick; // 小不点直接跳过坑
}
}
return arrMatch;
}
}
function test(strContent, strKeys) {
var s1 = new Date();
var arrMatch, item,
tblRoot = treeSearch.makeTree(strKeys),
t = new Date();
console.log( t - s1)
for (var i = 0; i < strContent.length; i++) {
item = strContent[i];
arrMatch = treeSearch.search(item, tblRoot);
}
console.log("time is: " + (new Date() - t) + "ms");
console.dir(arrMatch);
}
var s = (function() {
var Things = ['汉', '\n', '小', '大', '海', '能'];
var s = "";
var arr = []
for (var i = 1000000; i >= 0; i--) {
s += Things[parseInt(Math.random() * Things.length) % Things.length]
if( i % 50 == 0 ){
arr.push(s);
s = '';
}
};
return arr;
})()
test(s, ["汉能", "abcd", "bc", 'lala']);
var treeSearch = {
makeTree: function(strKeys) {
"use strict";
var tblCur = {},
tblRoot,
key,
str_key,
Length,
j,
i
;
tblRoot = tblCur;
/*一次性 把所以的关键字 都放在 hash tree中
abc, abd => { a:{ b: {c: {end:true}, d: {end: true } } } }
abcd, bc => { a:{b:{c:{d:{end:true}}}}, b:{c:{end:true}} }
*/
for ( j = strKeys.length - 1; j >= 0; j -= 1) {
str_key = strKeys[j];
Length = str_key.length;
for ( i = 0; i < Length; i += 1) {
key = str_key.charAt(i);
if (tblCur.hasOwnProperty(key)) { //生成子节点
tblCur = tblCur[key];
} else {
//在当前的对象 创建一个子对象
// 注意 tblRoot 和 tblCur是指向同一个对象 ori, 虽然tblCur移动了
// 但还是指向了对象 ori, 所以利用这个tblCur创建的子对象, 仍然是创建在ori上
// 而 tblRoot 也指向了 ori, 所以每次tblRoot
tblCur[key] = {}
//移动当前指针到 这个新的子对象
tblCur = tblCur[key];
}
}
tblCur.end = true; //最后一个关键字没有分割符
// 回溯, tblCur 指针从新指向根节点, 变成了整个对象
tblCur = tblRoot;
}
return tblRoot;
},
search: function(content, tblRoot) {
"use strict";
var tblCur,
p_star = 0,
n = content.length,
p_end,
match, //是否找到匹配
match_key,
match_str,
arrMatch = [], //存储结果
arrLength = 0 //arrMatch的长度索引
;
while (p_star < n) {
/*
tblRoot { a:{ b: {c: {end:true}, d: {end: true } } }, { } }
n : 10000
*/
tblCur = tblRoot; //回溯至根部
p_end = p_star;
match_str = "";
match = false;
do {
/* UCOKsfKBdaqRYQqSYUYjdvDR
match_key : U
*/
match_key = content.charAt(p_end);
// (向前移动) tblCur指针, 指向下一个 深层子对象
tblCur = tblCur[match_key]
if (!tblCur) {
//至少说明 以(a) 开头的字母的词组, 在当前位置是不可能匹配到了
p_star += 1;
// 确认安全后, 小不点 p_start 移动一个位置
break; //直接进入到下一次循环 ----> out
}
match_str += match_key; //保存 [match]
p_end += 1; // p_star 小不点, p_end 小不点的探测器 (向前移动)
if (tblCur.end === true) { //
match = true; //说明这几个木板下有坑
}
} while (true);
if (match === true) { //最大匹配
/**
把找到的 标记起来
**/
arrMatch[arrLength] = { //增强可读性
key: match_str,
begin: p_star - 1,
end: p_end
};
arrLength += 1;
p_star = p_end; // 小不点直接跳过坑
}
}
return arrMatch;
}
}
function test(strContent, strKeys) {
var s1 = new Date();
var arrMatch, item,
tblRoot = treeSearch.makeTree(strKeys),
t = new Date();
console.log( t - s1)
for (var i = 0; i < strContent.length; i++) {
item = strContent[i];
arrMatch = treeSearch.search(item, tblRoot);
}
console.log("time is: " + (new Date() - t) + "ms");
console.dir(arrMatch);
}
var s = (function() {
var Things = ['汉', '\n', '小', '大', '海', '能'];
var s = "";
var arr = []
for (var i = 1000000; i >= 0; i--) {
s += Things[parseInt(Math.random() * Things.length) % Things.length]
if( i % 50 == 0 ){
arr.push(s);
s = '';
}
};
return arr;
})()
test(s, ["汉能", "abcd", "bc", 'lala']);
链接 需要使用树算法,
var treeSearch = {
makeTree: function(strKeys) {
"use strict";
var tblCur = {},
tblRoot,
key,
str_key,
Length,
j,
i
;
tblRoot = tblCur;
for ( j = strKeys.length - 1; j >= 0; j -= 1) {
str_key = strKeys[j];
Length = str_key.length;
for ( i = 0; i < Length; i += 1) {
key = str_key.charAt(i);
if (tblCur.hasOwnProperty(key)) { //生成子节点
tblCur = tblCur[key];
} else {
tblCur = tblCur[key] = {};
}
}
tblCur.end = true; //最后一个关键字没有分割符
tblCur = tblRoot;
}
return tblRoot;
},
search: function(content, tblRoot) {
"use strict";
var tblCur,
p_star = 0,
n = content.length,
p_end,
match, //是否找到匹配
match_key,
match_str,
arrMatch = [], //存储结果
arrLength = 0 //arrMatch的长度索引
;
while (p_star < n) {
tblCur = tblRoot; //回溯至根部
p_end = p_star;
match_str = "";
match = false;
do {
match_key = content.charAt(p_end);
if (!(tblCur = tblCur[match_key])) { //本次匹配结束
p_star += 1;
break;
}else{
match_str += match_key;
}
p_end += 1;
if (tblCur.end === true) //是否匹配到尾部 //找到匹配关键字
{
match = true;
}
} while (true);
if (match === true) { //最大匹配
arrMatch[arrLength] = { //增强可读性
key: match_str,
begin: p_star - 1,
end: p_end
};
arrLength += 1;
p_star = p_end;
}
}
return arrMatch;
}
};
function test(strContent, strKeys) {
var arrMatch,
tblRoot = treeSearch.makeTree(strKeys),
t = new Date();
arrMatch = treeSearch.search(strContent, tblRoot);
console.log("time is: " + (new Date() - t) + "mm");
console.log(arrMatch);
}
var s = (function() {
var Things = [' ', '\n', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
var s = "";
for (var i = 1000000; i >= 0; i--) {
s += Things[parseInt(Math.random() * Things.length) % Things.length]
};
return s;
})()
test(s, ["abc", "efge", "fun", "tree"]);
var treeSearch = {
makeTree: function(strKeys) {
"use strict";
var tblCur = {},
tblRoot,
key,
str_key,
Length,
j,
i
;
tblRoot = tblCur;
for ( j = strKeys.length - 1; j >= 0; j -= 1) {
str_key = strKeys[j];
Length = str_key.length;
for ( i = 0; i < Length; i += 1) {
key = str_key.charAt(i);
if (tblCur.hasOwnProperty(key)) { //生成子节点
tblCur = tblCur[key];
} else {
//在当前的对象 创建一个子对象
tblCur[key] = {}
//移动当前指针到 这个新的子对象
tblCur = tblCur[key];
}
}
tblCur.end = true; //最后一个关键字没有分割符
tblCur = tblRoot;
}
return tblRoot;
},
search: function(content, tblRoot) {
"use strict";
var tblCur,
p_star = 0,
n = content.length,
p_end,
match, //是否找到匹配
match_key,
match_str,
arrMatch = [], //存储结果
arrLength = 0 //arrMatch的长度索引
;
while (p_star < n) {
/*
tblRoot { a:{ b: {c: {end:true}, d: {end: true } } }, { } }
n : 10000
*/
tblCur = tblRoot; //回溯至根部
p_end = p_star;
match_str = "";
match = false;
do {
/* UCOKsfKBdaqRYQqSYUYjdvDR
match_key : U
*/
match_key = content.charAt(p_end);
if (!(tblCur = tblCur[match_key])) { // (向前移动) tblCur指针, 指向下一个 深层子对象
p_star += 1; // 确认安全后, 小不点 p_start 移动一个位置
break; //直接进入到下一次循环 ----> out
}
match_str += match_key; //保存 [match]
p_end += 1; // p_star 小不点, p_end 小不点的探测器 (向前移动)
if (tblCur.end === true) { //
match = true; //说明这几个木板下有坑
}
} while (true);
if (match === true) { //最大匹配
/**
把找到的 标记起来
**/
arrMatch[arrLength] = { //增强可读性
key: match_str,
begin: p_star - 1,
end: p_end
};
arrLength += 1;
p_star = p_end; // 小不点直接跳过坑
}
}
return arrMatch;
}
}
function test(strContent, strKeys) {
var arrMatch,
tblRoot = treeSearch.makeTree(strKeys),
t = new Date();
arrMatch = treeSearch.search(strContent, tblRoot);
console.log("time is: " + (new Date() - t) + "mm");
console.dir(arrMatch[0]);
}
var s = "abcd汉能abcd"; /*(function() {
var Things = [' ', '\n', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
var s = "";
for (var i = 1000000; i >= 0; i--) {
s += Things[parseInt(Math.random() * Things.length) % Things.length]
};
return s;
})()*/
test(s, ["汉能", "abd"]);
如何在100万文字的文章中 200ms内 快速提取 替换 上万个关键字的更多相关文章
- SQL 从100万条记录中的到 成绩最高的记录
从100万条记录中的到 成绩最高的记录 问题分析:要从一张表中找到成绩最高的记录并不难,有很多种办法,最简单的就是利用TOP 1 select top 1 * from student order b ...
- Qt中提高sqlite的读写速度(使用事务一次性写入100万条数据)
SQLite数据库本质上来讲就是一个磁盘上的文件,所以一切的数据库操作其实都会转化为对文件的操作,而频繁的文件操作将会是一个很好时的过程,会极大地影响数据库存取的速度.例如:向数据库中插入100万条数 ...
- zt:如何快速赚取人生第一个100万?
投递人 itwriter 发布于 2016-06-20 23:43 评论(16) 有1795人阅读 原文链接 [收藏] « » 不久前我开始使用「分答」,将回答问题的价格设置成 10 元.花最长 1 ...
- 极限挑战—C#+ODP 100万条数据导入Oracle数据库仅用不到1秒
链接地址:http://www.cnblogs.com/armyfai/p/4646213.html 要:在这里我们将看到的是C#中利用ODP实现在Oracle数据库中瞬间导入百万级数据,这对快速批量 ...
- 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)
原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方 ...
- C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)
作者: Aicken(李鸣) 来源: 博客园 发布时间: 2010-09-08 15:00 阅读: 4520 次 推荐: 0 原文链接 [收藏] 摘要: ...
- python 统计MySQL大于100万的表
一.需求分析 线上的MySQL服务器,最近有很多慢查询.需要统计出行数大于100万的表,进行统一优化. 需要筛选出符合条件的表,统计到excel中,格式如下: 库名 表名 行数 db1 users 1 ...
- Netty 100万级到亿级流量 高并发 仿微信 IM后台 开源项目实战
目录 写在前面 亿级流量IM的应用场景 十万级 单体IM 系统 高并发分布式IM系统架构 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -10[ 博客园 总入口 ] 写在前面 大家好 ...
- Netty 100万级高并发服务器配置
前言 每一种该语言在某些极限情况下的表现一般都不太一样,那么我常用的Java语言,在达到100万个并发连接情况下,会怎么样呢,有些好奇,更有些期盼. 这次使用经常使用的顺手的netty NIO框架(n ...
随机推荐
- JS 中的substring ,substr ,slice,split,join
substr with different arguments passed in: str.substring(startNum,stopNum ); str.slice(startNum,stop ...
- 一键配置IP地址脚本
#/bin/bash NETPWD='/etc/sysconfig/network-scripts/' read -p "please enten net num(1,2,3,4) : &q ...
- 《Maven实战》第10章 使用Maven进行测试
10.2maven-surefire-plugin插件 [生命周期]的[阶段]与[插件]的[目标]绑定 default生命周期的test阶段:使用单元测试框架运行测试 Maven内置绑定:defaul ...
- Linux(CentOS)中常用软件安装,使用及异常——XManager, 中文支持,JDK
XManager图形化界面远程连接 采用Xshell的方式可以不用在CentOS系统中配置即可以相连,主要原理就是SSH连接的方式,但是XManager图形化界面远程连接是需要修改CentOS系统的. ...
- SpringBoot2.0+ElasticSearch网盘搜索实现
1.ES是如何实现分布式高并发全文检索 2.简单介绍ES分片Shards分片技术 3.为什么ES主分片对应的备分片不在同一台节点存放 4.索引的主分片定义好后为什么不能做修改 5.ES如何实现高可用容 ...
- Autofac register and resolve
Passing Parameters to Register When you register components you have the ability to provide a set of ...
- Luogu-3527 [POI2011]MET-Meteors
Luogu-3527 [POI2011]MET-Meteors 题面 Luogu-3527 题解 感觉和上一那道题是一个类型的,直接二分答案,用BIT维护区间加(差分)即可 代码 #include&l ...
- Google maps api demo 2
demo /** * @fileoverview Sample showing capturing a KML file click * and displaying the contents in ...
- Jquery.LazyLoad.js实现图片延迟加载功能
从网上下载来的版本多多少少都有些BUG,尤其是加载后在IE6和IE7下图片闪动是个大问题,在网上查了很久,也没有找到相关的解决方案.没解决方案,就得发挥咱DIY的精神,自己想法解决,分析了BUG,理了 ...
- 一篇文章学会spark-streaming
版权申明:转载请注明出处.文章来源:bigdataer.net 1.什么是spark-streaming? 实际生产中会有许多应用到实时处理的场景,比如:实时监测页面点击,实时监测系统异常,实时监测来 ...