Скажем, у меня есть два списка или массивы строк. Например:
список 1: "a", "c", "b", "d" , "f" , "e"
список 2: "a", "d" , "e" , "f" , "h"
Список 1 и список 2 имеют произвольную длину. Список 1 может содержать элементы, не входящие в список 2, и наоборот.
Я хотел бы знать, когда элементы в списке 1 находятся в списке 2, и более конкретно я хочу знать, когда элемент в списке 1 найден в списке 2, но найден в другом порядке, чем он найден в списке 2, относительно элементов в списке 2. (Надеюсь, приведенный ниже пример поясняет это утверждение).
Например, "a" находится в обоих списках и является первым элементом в обоих списках. Итак, до сих пор все в порядке. "c" и "b" находятся только в первом списке, поэтому их можно игнорировать. "h" находится только во втором списке, и поэтому его можно игнорировать.
"d" находится как в первом, так и во втором списке. Он найден после "a" (первый элемент) в исходном списке. Несмотря на то, что позиция в первом списке отличается от второго списка, это нормально, потому что его относительный порядок одинаковый в двух списках (второе совпадение между списками).
В приведенном выше примере "f" и "e" находятся в "неправильном" порядке в списке 1, потому что "e" приходит до "f" во втором списке. Поэтому я хотел бы сообщить, что "e" и "f" находятся в неправильном порядке в первом списке. Как мне это сделать?
Решение должно быть в С#. Спасибо!
string[] list1 = {"a", "c", "b", "d", "f", "e"};
string[] list2 = {"a", "d", "e", "f", "h"};
int i = 0;
var list1a = list1.Intersect(list2).Select(l=> new { item = l, order= i++});
int j = 0;
var list2a = list2.Intersect(list1).Select(l=> new { item = l, order= j++});
var r = from l1 in list1a join l2 in list2a on l1.order equals l2.order
where l1.item != l2.item
select new {result=string.Format("position {1} is item [{0}] on list1 but its [{2}] in list2", l1.item, l1.order, l2.item )};
r.Dump();
позиция 2 - это элемент [f] в списке1, но его [e] в списке2
позиция 3 - это элемент [e] в списке1, но его [f] в списке2
Левенштейн расстояние? Я думаю, что первое решение, которое вы уже приняли, имеет недостаток. Он скажет вам, что все не в порядке, даже если одна маленькая вещь вставлена:
string[] list1 = { "a", "c", "b", "d", "j", "e", "f" };
string[] list2 = { "a", "d", "e", "f", "h", "j" };
который говорит, что j, e, f не соответствуют порядку, поскольку j был вставлен.
Это указывает на проблему, с которой вы сталкиваетесь. Существует множество решений, даже более чем одного оптимального решения, для проблемы того, что не соответствует порядку. Является ли порядок J или e и f? Все ли они не в порядке? Есть что-то, называемое алгоритм расстояния Levenshtein, который находит минимальное количество вставок и операций удаления, необходимых для начала работы с множеством A и заканчивается набором B Есть несколько лучших решений, это просто находит один из них.
Этот следующий алгоритм корректно выводит, что в list1, j был вставлен и e, f сдвинуты, но все еще в правильном порядке.
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Math = System.Math;
namespace LevCompareLists {
class Program {
static void Main(string[] args) {
string[] list1 = { "a", "c", "b", "d", "j", "e", "f" };
string[] list2 = { "a", "d", "e", "f", "h", "j" };
int?[] aMap21 = Levenshtein(list2, list1);
int?[] aMap12 = Levenshtein(list1, list2);
}
public static int?[] Levenshtein(string[] Src, String[] Dst) {
// this finds a minimum difference solution of inserts and deletes that maps Src to Dst
// it returns the map from the perspective of Dst, i.e.:
// each element of the return array contains the Src index of the corresponging element in B
// a null value means the element in B was inserted, and never existed in A
//
// Given A = {"a", "c", "b", "d", "j", "e", "f"}
// B = {"a", "d", "e", "f", "h", "j"};
//
// Levenshtein(B, A):
// a c b d j e f <-- A
// 0 1 2 3 <-- aMap
// a d e f h j <-- B
//
// Levenshtein(A, B):
// a d e f h j <-- B
// 0 3 5 6 <-- aMap
// a c b d j e f <-- A
//
// see: http://en.wikipedia.org/wiki/Levenshtein_distance
int cSrc = Src.Length; //length of s
int cDst = Dst.Length; //length of t
if (cSrc == 0 || cDst == 0) return null;
//**** create the Levenshtein matrix
// it has at 1 extra element in each dimension to contain the edges
int[,] aLev = new int[cSrc + 1, cDst + 1]; // the matrix
int iSrc, iDst;
// Load the horizontal and vertical edges
for (iSrc = 0; iSrc <= cSrc; aLev[iSrc, 0] = iSrc++) ;
for (iDst = 0; iDst <= cDst; aLev[0, iDst] = iDst++) ;
// load the interior
for (iSrc = 1; iSrc <= cSrc; iSrc++)
for (iDst = 1; iDst <= cDst; iDst++)
aLev[iSrc, iDst] = Math.Min(Math.Min(aLev[iSrc - 1, iDst] + 1, aLev[iSrc, iDst - 1] + 1),
aLev[iSrc - 1, iDst - 1] + ((Dst[iDst - 1] == Src[iSrc - 1]) ? 0 : 2));
DumpLevMatrix(aLev, Src, Dst); // Debug
//**** create the return map, using the Levenshtein matrix
int?[] aMap = new int?[cDst]; // this is the return map
iSrc = cSrc; // start in lower right corner of the Levenshtein matrix
iDst = cDst; // start in lower right corner of the Levenshtein matrix
// work backwards to pick best solution
while ((iSrc >= 0) || (iDst >= 0)) {
if ((iSrc > 0) && (iDst > 0)) {
// enter here if iSrc and iDst are in the lev matrix and not on its edge
int nCur = aLev[iSrc, iDst];
int nIns = nCur - aLev[iSrc, iDst - 1]; // if move along B to find match, it was an insert
int nDel = nCur - aLev[iSrc - 1, iDst]; // if move along A to find match, it was a deletion
if (nIns == 1) // this char was NOT in A, but was inserted into B
iDst--; // Leave map of B[j] to nowher, scan to previous B (--j)
else if (nDel == 1) // this char was in A, but is missing in B
iSrc--; // Don't map any B, scan to previous A (--i)
else // Match
aMap[iDst-- - 1] = iSrc-- - 1; // After map B[j] to A[i], scan to prev A,B (--i, --j)
} else {
if (iDst > 0) // remaining chars are inserts, Leave map of B[j] to nowher, scan to previous B (--j)
iDst--;
else if (iSrc > 0) // Delete to the end, deletes do nothing
iSrc--;
else
break;
}
}
DumpMap(aMap, Dst); // Debug
return aMap;
}
// just for debugging
static void DumpLevMatrix(int[,] aLev, string[] Src, string[] Dst) {
StringBuilder sb = new StringBuilder();
int cSrc = Src.Length;
int cDst = Dst.Length;
int iSrc, iDst;
sb.Length = 6;
for (iDst = 0; iDst < cDst; ++iDst)
sb.AppendFormat("{0,-3}", Dst[iDst]);
Console.WriteLine(sb.ToString());
for (iSrc = 0; iSrc <= cSrc; ++iSrc) {
if (iSrc == 0)
sb.Length = 3;
else {
sb.Length = 0;
sb.AppendFormat("{0,-3}", Src[iSrc - 1]);
}
for (iDst = 0; iDst <= cDst; ++iDst)
sb.AppendFormat("{0:00}", aLev[iSrc, iDst]).Append(" ");
Console.WriteLine(sb.ToString());
}
}
// just for debugging
static void DumpMap(int?[] aMap, string[] Dst) {
StringBuilder sb = new StringBuilder();
for (int iMap = 0; iMap < aMap.Length; ++iMap)
sb.AppendFormat("{0,-3}", Dst[iMap]); // dst and map are same size
Console.WriteLine(sb.ToString());
sb.Length = 0;
for (int iMap = 0; iMap < aMap.Length; ++iMap)
if (aMap[iMap] == null)
sb.Append(" ");
else
sb.AppendFormat("{0:00}", aMap[iMap]).Append(" ");
Console.WriteLine(sb.ToString());
}
}
}
Как насчет
list1.Intersect(list2).SequenceEquals(list2.Intersect(list1))
У меня нет кода, но это должны быть два основных шага:
Возможно, это даже поможет очистить оба списка. Затем вы можете использовать указатель для каждого списка, установить его в первый элемент и увеличить их до появления несоответствия.
Как насчет этого:
string[] list1 = { "a", "c", "b", "d", "f", "e" };
string[] list2 = { "a", "d", "e", "f", "h" };
var indexedList1 = list1.Select((x, i) => new
{
Index = i,
Item = x
});
var indexedList2 = list2.Select((x, i) => new
{
Index = i,
Item = x
});
var intersectedWithIndexes = indexedList2
.Join(indexedList1,
x => x.Item,
y => y.Item,
(x, y) => new
{
ExpectedIndex = x.Index,
ActualIndex = y.Index,
x.Item
})
.Where(x => x.ActualIndex != x.ExpectedIndex)
.ToArray();
var outOfOrder = intersectedWithIndexes
.Select((x, i) => new
{
Item = x,
index = i
})
.Skip(1)
.Where(x => x.Item.ActualIndex < intersectedWithIndexes[x.index - 1].ActualIndex ||
x.Item.ExpectedIndex < intersectedWithIndexes[x.index - 1].ExpectedIndex)
.Select(x => new
{
ExpectedBefore = x.Item,
ExpectedAfter = intersectedWithIndexes[x.index - 1]
});
foreach (var item in outOfOrder)
{
Console.WriteLine("'{0}' and '{1}' are out of order at index {2}",
item.ExpectedBefore.Item,
item.ExpectedAfter.Item,
item.ExpectedBefore.ActualIndex);
}
выход:
'f' and 'e' are out of order at index 4