Files
SnowLuma/.pnpm-store/v11/files/1d/d43a04e2f7ef33b7796413ccd0d6f9aae9777827af5f2361ee2955d4cdca8d89ca9febac9d93f8b672feff6c952cfbcbea8387eb4d9d63e17ca25b15834611
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

144 lines
3.8 KiB
Plaintext

'use strict';
class CleanPasswords {
constructor({
substr,
limit,
trieRoot
}) {
this.buffer = [];
this.finalPasswords = [];
this.substr = substr;
this.limit = limit;
this.trieRoot = trieRoot;
}
getAllPossibleSubsAtIndex(index) {
const nodes = [];
let cur = this.trieRoot;
for (let i = index; i < this.substr.length; i += 1) {
const character = this.substr.charAt(i);
cur = cur.getChild(character);
if (!cur) {
break;
}
nodes.push(cur);
}
return nodes;
}
// eslint-disable-next-line complexity,max-statements
helper({
onlyFullSub,
isFullSub,
index,
subIndex,
changes,
lastSubLetter,
consecutiveSubCount
}) {
if (this.finalPasswords.length >= this.limit) {
return;
}
if (index === this.substr.length) {
if (onlyFullSub === isFullSub) {
this.finalPasswords.push({
password: this.buffer.join(''),
changes,
isFullSubstitution: onlyFullSub
});
}
return;
}
// first, exhaust all possible substitutions at this index
const nodes = [...this.getAllPossibleSubsAtIndex(index)];
let hasSubs = false;
// iterate backward to get wider substitutions first
for (let i = index + nodes.length - 1; i >= index; i -= 1) {
const cur = nodes[i - index];
const sub = cur.parents.join('');
if (cur.isTerminal()) {
// Skip if this would be a 4th or more consecutive substitution of the same letter
// this should work in all language as there shouldn't be the same letter more than four times in a row
// So we can ignore the rest to save calculation time
if (lastSubLetter === sub && consecutiveSubCount >= 3) {
continue;
}
hasSubs = true;
const letters = cur.subs;
for (const letter of letters) {
this.buffer.push(letter);
const newSubs = changes.concat({
i: subIndex,
letter,
substitution: sub
});
// recursively build the rest of the string
this.helper({
onlyFullSub,
isFullSub,
index: index + sub.length,
subIndex: subIndex + letter.length,
changes: newSubs,
lastSubLetter: sub,
consecutiveSubCount: lastSubLetter === sub ? consecutiveSubCount + 1 : 1
});
// backtrack by ignoring the added postfix
this.buffer.pop();
if (this.finalPasswords.length >= this.limit) {
return;
}
}
}
}
// next, generate all combos without doing a substitution at this index
// if a partial substitution is requested or there are no substitutions at this index
if (!onlyFullSub || !hasSubs) {
const firstChar = this.substr.charAt(index);
this.buffer.push(firstChar);
this.helper({
onlyFullSub,
isFullSub: isFullSub && !hasSubs,
index: index + 1,
subIndex: subIndex + 1,
changes,
lastSubLetter,
consecutiveSubCount
});
this.buffer.pop();
}
}
getAll() {
// only full substitution
this.helper({
onlyFullSub: true,
isFullSub: true,
index: 0,
subIndex: 0,
changes: [],
lastSubLetter: undefined,
consecutiveSubCount: 0
});
// only partial substitution
this.helper({
onlyFullSub: false,
isFullSub: true,
index: 0,
subIndex: 0,
changes: [],
lastSubLetter: undefined,
consecutiveSubCount: 0
});
return this.finalPasswords;
}
}
const getCleanPasswords = (password, limit, trieRoot) => {
const helper = new CleanPasswords({
substr: password,
limit,
trieRoot
});
return helper.getAll();
};
module.exports = getCleanPasswords;
//# sourceMappingURL=getCleanPasswords.cjs.map