我想识别仅由相同长度的字符组组成的字符串。这些组中的每一个都包含至少两个相同的字符。所以,这里有一些例子:
aabbcc true
abbccaa false
xxxrrrruuu false (too many r's)
xxxxxfffff true
aa true (shortest possible positive example)
aabbbbcc true // I added this later to clarify my intention
@ilkkachu:感谢您关于重复相同角色组的评论。我添加了上面的例子。是的,我希望将最后一个样本测试为true:由两个字母组aa, bb, bb, cc
组成的字符串。
是否有一种简单的方法可以使用正则表达式和JavaScript对字符串应用此条件检查?
我的第一次尝试是做
之类的事情var strarr=['aabbcc','abbccaa','xxxrrrruuu',
'xxxxxfffff','aa','negative'];
var rx=/^((.)\2+)+$/;
console.log(strarr.map(str=>str+': '+!!str.match(rx)).join('\n'));
它 查找重复字符组,但是不但是注意这些组相同的长度,如输出所示:
aabbcc: true
abbccaa: false
xxxrrrruuu: true // should be false!
xxxxxfffff: true
aa: true
aabbbbcc: true
negative: false
如何检查相同长度的字符组?
答案 0 :(得分:9)
要获得相同角色的所有组都有一个简单的正则表达式解决方案:
/(.)\1*/g
只需重复捕获组1中角色的反向引用\1
。
然后检查数组中是否有相同字符串的长度不匹配。
示例摘录
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g) //array with same character strings
.map(function(x){return x.length}); //array with lengths
let smallest_length = arr.reduce(function(x,y){return x < y ? x : y});
if(smallest_length === 1) return false;
return arr.some(function(n){return (n % smallest_length) !== 0}) == false;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
具有胖箭头的ECMAScript 6版本(在IE中不起作用)
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g)
.map((x) => x.length);
let smallest_length = arr.reduce((x,y) => x < y ? x : y);
if(smallest_length === 1) return false;
return arr.some((n) => (n % smallest_length) !== 0) == false;
}
或者使用exec代替匹配,对于大字符串应该更快 因为它可以在找到不同长度后立即退出while循环 但这样做的缺点是,在比较它们之前,它无法获得所有长度的最小长度 所以那些最后长度最小的人就不会这样。
function sameLengthCharGroups(str)
{
if(!str) return false;
const re = /(.)\1*/g;
let m, smallest_length;
while(m = re.exec(str)){
if(m.index === 0) {smallest_length = m[0].length}
if(smallest_length > m[0].length && smallest_length % m[0].length === 0){smallest_length = m[0].length}
if(m[0].length === 1 ||
// m[0].length !== smallest_length
(m[0].length % smallest_length) !== 0
) return false;
}
return true;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
答案 1 :(得分:5)
这是一个以线性时间运行的:
function test(str) {
if (str.length === 0) return true;
let lastChar = str.charAt(0);
let seqLength = 1;
let lastSeqLength = null;
for (let i = 1; i < str.length; i++) {
if (str.charAt(i) === lastChar) {
seqLength++;
}
else if (lastSeqLength === null || seqLength === lastSeqLength) {
lastSeqLength = seqLength;
seqLength = 1;
lastChar = str.charAt(i);
}
else {
return false;
}
}
return (lastSeqLength === null || lastSeqLength === seqLength);
}
答案 2 :(得分:1)
使用粘性标记y
和replace
方法,您可以更快地完成此操作。这个技巧用空字符串替换第一个长度的出现(并且一旦出现不同长度的事件就停止)然后检查是否还剩下一些字符:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w => {
console.log(w + " => " + (w.replace(/(.)\1+/gy, ($0, $1, o) => {
return $0.length == (o == 0 ? l = $0.length : l) ? '' : $0;
}).length < 1));
});
&#13;
答案 3 :(得分:1)
另一种解决方法是使用replace()
和test()
。第一个用相应的长度替换不同的字符,第二个在前面的字符串中查找相同的重复数字:
var str = 'aabbc';
/^(\d+\n)\1*$/.test(str.replace(/(.)\1+/gy, x => x.length + '\n'));
演示:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w =>
console.log(/^(\d+\n)\1*$/.test(w.replace(/(.)\1+/gy, x => x.length + '\n')))
);
答案 4 :(得分:1)
由于要求已经改变或者现在不明确,这是我发布的第三个解决方案。要接受可以分为较小组的字符串,例如aabbbb
,我们可以:
2
和4
所有不同字符的所有长度。d
。m
的集合中找到最小长度。d
中的所有值在除以m
演示
var words = ['aabbbcccdddd', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aab', 'aabbbbccc'];
words.forEach(w => {
var d = [], m = Number.MAX_SAFE_INTEGER;
var s = w.replace(/(.)\1+/gy, x => {
d.push(l = x.length);
if (l < m) m = l;
return '';
});
console.log(w + " => " + (s == '' && !d.some(n => n % m != 0)));
});
答案 5 :(得分:0)
相同字符的重复模式的长度需要在正则表达式中指定。下面的代码片段创建正则表达式,查找字符串长度为11到2。一旦找到匹配项就退出for循环,函数返回找到的模式的长度:
function pat1(s){
for (var i=10;i;i--)
if(RegExp('^((.)\\2{'+i+'})+$').exec(s))
return i+1;
return false;}
如果找不到任何内容,则会返回false
。
如果不需要模式的长度,也可以在 one go中设置正则表达式(不需要围绕它进行for循环):
function pat2(s){
var rx=/^((.)\2)+$|^((.)\4{2})+$|^((.)\6{4})+$|^((.)\8{6})+$/;
return !!rx.exec(s);
}
以下是两项测试的结果:
console.log(strarr.map(str=>
str+': '+pat1(str)
+' '+pat2(str)).join('\n')+'\n');
aabbcc: 2 true
abbccaa: false false
xxxrrrruuu: false false
xxxxxfffff: 5 true
aa: 2 true
aabbbbcc: 2 true
negative: false false
pat2中的正则表达式仅查找某些重复计数。当找到前一个字符的1,2,4或6次重复时,结果为正。找到的模式长度为2,3,5或7个字符(素数!)。通过这些长度检查,任何可由这些数字之一划分的模式长度都将被发现为正数(2,3,4,5,6,7,8,9,10,12,14,15,16,18,20) ,21,22,24,......)。
答案 6 :(得分:0)
由于正则表达式从未成为我的强项,因此使用String#replace()
在更改字母时将字符串添加到字符串然后使用它来拆分为数组并检查数组中的所有元素是否具有相同的长度
const values = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
const expect = [true, false, false, true, true];
const hasMatchingGroups = (str) => {
if(!str || str.length %2) return false;
const groups = str.replace(/[a-z]/g,(match, offset, string) => {
return string[offset + 1] && match !== string[offset + 1] ? match + '|' : match;
}).split('|');
return groups.every(s => s.length === groups[0].length)
}
values.forEach((s, i) => console.log(JSON.stringify([s,hasMatchingGroups(s), expect[i]])))