У меня есть массив строк с именем «коллекция», как показано ниже. Я хочу перебрать массив и найти строки с наибольшим перекрытием - это самые одинаковые символы в строке. Как только я найду две строки, я хочу добавить новую строку в массив с конкатенацией этих двух строк, а затем удалить исходные строки. Загвоздка в том, что конкатенация должна игнорировать общие символы. Например: «hello wo» и «o world» станут «hello world». Я хочу повторять этот процесс, пока не останется одна строка.
let collection = ["all is well", "ell that en", "hat end", "t ends well"];
let longestOverlap = 0;
let longestOverlapChars = '';
for (i = 0 ; i < collection.length ; i++){
for (j= i+1 ; j < collection.length ; j++){
findOverlap(collection[i],collection[j]);
}
}
function findOverlap(a, b, originalb) {
if (a.indexOf(b) >= 0) {
if (longestOverlapChars.length < b.length){
longestOverlapChars = b;
longestOverlap = longestOverlapChars.length;
console.info(longestOverlapChars, longestOverlap);
}
return console.info(a && originalb) ;
}
return findOverlap(a, b.substring(0, b.length - 1));
}
Мои результаты показывают 4 результата в консоли: элл, шляпа эн, тен эн, т конец.
Это показывает случаи перекрытия текста.
Впоследствии «шляпа en» является самой большой, поэтому я хочу объединить две строки, которые имеют это перекрытие. Итак, мой новый массив строк будет выглядеть так: [" все хорошо", "черт возьми, конец", "хорошо кончается"];
Теперь есть 3 строки, и я хочу повторять этот процесс, пока не получу ["все хорошо, что хорошо кончается"].
Может ли кто-нибудь дать мне какое-то направление с моим кодом? Кроме того, любые советы по кодированию будут полезны. Пытаюсь улучшить.
Вот как это может работать:
/*
* findOverlap:
* "merge" two strings, where the overlapping part is not repeated.
* Input:
* a, b: the strings to "merge"
* retry: this is only used by the internal call. It serves to execute the
* function a second time with the two strings in reversed positions
* Returns:
* The merged string. If there is no overlap found, then just join the two
* Examples:
* findOverlap("abcdef", "defghi") === "abcdefghi"
* findOverlap("aaa", "zzz") === "aaazzz"
*/
function findOverlap(a, b, retry = true) {
// If one of the two strings is empty, return the other one. This ensures
// that empty strings in the collection do not influence the result.
if (!a || !b) return a || b;
// Find the position in a, of the first character of b
let i = a.indexOf(b[0]);
while (i > -1) { // As long as there is such an occurrence...
// Calculate what the size of the overlapping part would be:
// This is either the size of the remaining part of a, or
// the size of b, when it is a real substring of a:
let size = Math.min(a.length - i, b.length);
// See if we have an overlap at this position:
if (a.slice(i).slice(0, size) === b.slice(0, size)) {
// Yes! Include the "overflowing" part of b:
return a + b.slice(size);
}
// No match. Try a next position:
i = a.indexOf(b[0], i+1);
}
// The start of b does not overlap with the end of a, so try
// the opposite:
if (retry) return findOverlap(b, a, false); // reversed args
// If this was already the reversed case, then just concatenate
return b+a; // Reverse again
}
/*
* findLongestOverlap:
* find information about the two strings that have the longest overlap.
* Input:
* collection: an array of strings
* Returns:
* An object with 4 properties:
* merged: the merged string, not repeating the overlapping part
* i, j: the two indexes in the collection of the contributing strings
* overlapSize: number of characters that are part of the overlap
* Example:
* findLongestOverlap(["abcdef", "defghi", "cdefg"]) returns:
* { merged: "abcdefg", i: 0, j: 2, overlapSize: 4 }
*/
function findLongestOverlap(collection) {
// Initialise the "best" overlap we have so far (we don't have any yet)
let longest = { overlapSize: -1 };
// Iterate all pairs from the collection:
for (let j = 1; j < collection.length; j++) {
let b = collection[j];
for (let i = 0; i < j; i++) {
let a = collection[i];
const merged = findOverlap(a, b);
// Derive the size of the overlap from the merged string:
const overlapSize = a.length + b.length - merged.length;
// Did we improve?
if (overlapSize > longest.overlapSize) {
// Yes! So keep track of all info we need:
longest = { merged, i, j, overlapSize };
}
}
}
return longest;
}
/*
* reduceToOne:
* merge a series of strings, merging via the greatest overlaps first.
* Input:
* any number of arguments: all strings
* Returns:
* A single string, which represents the merge
*/
function reduceToOne(...collection) { // Grab all arguments as an array
// Repeat until the collection is reduced to one element
for (let i = collection.length; --i; ) {
// Get information from best possible pair-merge:
const { merged, i, j } = findLongestOverlap(collection);
// Remove the original two strings having the longest overlap
collection.splice(j, 1);
collection.splice(i, 1);
// Add the merged string
collection.push(merged);
}
// Return the single string that remains
return collection[0];
}
// Example run:
let collection = ["all is well", "ell that en", "hat end", "t ends well"];
const result = reduceToOne(...collection); // Spread the array as individual arguments
console.info(result);
Прокомментируйте свой код:
longestOverlapChars
и longestOverlap
нарушают эту передовую практику.findOverlap
работает нормально, когда конец a
перекрывается с началом b
, но она не сможет найти перекрытие, если конецb
перекрывается с Началоa
.Вы можете взять функцию, которая получает длину раскрытия, новую строку и индексы соответствующей строки, а затем фильтрует информацию о максимальном перекрытии, фильтрует коллекцию и добавляет новую строку в коллекцию.
function findOverlap(a, b) {
function partSubs(a, b, pos) {
var i = b.length;
do {
if (i && a.endsWith(b.slice(0, i))) return [i, a + b.slice(i), pos];
} while (i--)
}
// check full sub
if (a.includes(b)) return [Infinity, a, pos];
if (b.includes(a)) return [Infinity, b, pos];
// get partSubs
var temp = [partSubs(a, b, [i, j]), partSubs(b, a, [j, i])];
if (temp[0] && temp[1]) return temp[0][0] > temp[1][0] ? temp[0] : temp[1];
return temp[0] || temp[1];
}
var collection = ["all is well", "ell that en", "hat end", "t ends well"],
overlap,
i, j,
longest;
while (collection.length > 1) {
overlap = [];
for (i = 0; i < collection.length - 1; i++)
for (j = i + 1; j < collection.length; j++)
overlap.push(findOverlap(collection[i], collection[j]));
longest = overlap.reduce((a, b) => a && b ? a[0] > b[0] ? a : b : a || b);
collection = collection.filter((_, i) => !longest[2].includes(i));
collection.push(longest[1]);
}
console.info(collection);
Вау, спасибо. Это здорово. Не могли бы вы оставить еще несколько комментариев к коду, чтобы я знал, что делает каждая строка?