2 Stimmen

Implementierung eines effizienten Algorithmus zur Ermittlung der Schnittmenge zweier Zeichenketten

Implementieren Sie einen Algorithmus, der zwei Zeichenketten als Eingabe nimmt und die Schnittmenge der beiden zurückgibt, wobei jeder Buchstabe höchstens einmal vorkommt.

Algo: (die verwendete Sprache wird c# sein)

  1. Beide Zeichenketten in ein char-Array umwandeln
  2. das kleinere Array zu nehmen und eine Hash-Tabelle dafür zu erstellen, wobei der Schlüssel das Zeichen und der Wert 0 ist
  3. Nun durchlaufen Sie das andere Array und erhöhen die Anzahl in der Hashtabelle, wenn das Zeichen darin vorhanden ist.
  4. Nun werden alle Zeichen für die Hashtabelle entfernt, deren Wert > 0 ist.
  5. Dies sind Schnittmengenwerte.

Dies ist eine O(n)-Lösung, die jedoch zusätzlichen Platz, 2 Char-Arrays und eine Hashtabelle benötigt

Fällt euch eine bessere Lösung als diese ein?

3 Stimmen

Er schlägt bereits einen Algorithmus vor und fragt, ob jemand weiß, wie man es besser machen kann.

0 Stimmen

Hey....siehe mein Algo oben, ich muss wissen, ob wir dies in O(n)-Zeit lösen können, ohne zusätzlichen Platz zu verwenden

0 Stimmen

Ich weiß nicht C#, so dass ich nicht weiß, aber wäre dies nicht perfekt für ein Set (wie in Java oder Python gefunden) sein?

11voto

JP Alioto Punkte 44283

Wie wäre es damit ...

var s1 = "aabbccccddd";
var s2 = "aabc";

var ans = s1.Intersect(s2);

0 Stimmen

Das ist zwar der einfache Weg, aber der OP fragt nach einem Algorithmus, der dies direkt tut.

0 Stimmen

@Ahmad: wenn du den Code willst, benutze einfach Reflector, um zu sehen, wie Intersect implementiert ist :)

0 Stimmen

@JP: Stimmt, diese Antwort und Reflector sind mir auch zuerst in den Sinn gekommen :)

2voto

Phil Punkte 174

Ich habe das nicht getestet, aber hier ist mein Gedanke:

  1. Quicksortieren Sie beide Zeichenketten an Ort und Stelle, so dass Sie eine geordnete Folge von Zeichen haben
  2. Behalten Sie einen Index in beiden Zeichenfolgen, vergleichen Sie das "nächste" Zeichen aus jeder Zeichenfolge, wählen Sie das erste aus und geben Sie es aus, wobei der Index für diese Zeichenfolge erhöht wird.
  3. Fahren Sie fort, bis Sie das Ende einer der Zeichenketten erreicht haben, und ziehen Sie dann einfach eindeutige Werte aus dem Rest der verbleibenden Zeichenkette.

Benötigt keinen zusätzlichen Speicher, sondern nur die beiden ursprünglichen Zeichenketten, zwei Ganzzahlen und eine Ausgabezeichenkette (oder einen StringBuilder). Als zusätzlicher Bonus werden die Ausgabewerte auch noch sortiert!

Teil 2: Das würde ich schreiben (sorry für die Kommentare, bin neu bei stackoverflow):

private static string intersect(string left, string right)
{
  StringBuilder theResult = new StringBuilder();

  string sortedLeft = Program.sort(left);
  string sortedRight = Program.sort(right);

  int leftIndex = 0;
  int rightIndex = 0;

  //  Work though the string with the "first last character".
  if (sortedLeft[sortedLeft.Length - 1] > sortedRight[sortedRight.Length - 1])
  {
    string temp = sortedLeft;
    sortedLeft = sortedRight;
    sortedRight = temp;
  }

  char lastChar = default(char);
  while (leftIndex < sortedLeft.Length)
  {
    char nextChar = (sortedLeft[leftIndex] <= sortedRight[rightIndex]) ? sortedLeft[leftIndex++] : sortedRight[rightIndex++];

    if (lastChar == nextChar) continue;

    theResult.Append(nextChar);
    lastChar = nextChar;
  }

  //  Add the remaining characters from the "right" string
  while (rightIndex < sortedRight.Length)
  {
    char nextChar = sortedRight[rightIndex++];
    if (lastChar == nextChar) continue;

    theResult.Append(nextChar);
    lastChar = nextChar;
  }
  theResult.Append(sortedRight, rightIndex, sortedRight.Length - rightIndex);

  return (theResult.ToString());
}

Ich hoffe, das macht mehr Sinn.

0 Stimmen

Zumindest bei extrem großen Datensätzen kann man es besser machen als mit Quicksort, wenn man weiß, dass die Daten Zeichen sind. Wenn Sie mit wirklich riesigen Strings zu tun haben, könnten Sie eine Bibliothek sortieren und O(n)-Leistung erhalten, indem Sie im Grunde ein 255-Zeichen-Array erstellen.

0 Stimmen

Ich habe nicht verstanden: "Behalten Sie einen Index in beiden Zeichenketten, vergleichen Sie das "nächste" Zeichen aus jeder Zeichenkette, wählen Sie das erste aus und geben Sie es aus, wobei der Index für diese Zeichenkette erhöht wird." Können Sie etwas mehr Licht in die Sache bringen?

0 Stimmen

Ich meinte etwa so: char lastChar = default(char); while (leftIndex < sortedLeft.Length) { char nextChar = (rightIndex >= sortedRight.Length) || (sortedLeft[leftIndex] <= sortedRight[rightIndex]) ? sortedLeft[leftIndex++] : sortedRight[rightIndex++]; if (lastChar == nextChar) continue; theResult.Append(nextChar); lastChar = nextChar; } Ich hoffe, das ist klar. Ich habe es jetzt tatsächlich geschrieben und es funktioniert gut, außer dass ich entdeckt habe, dass eine In-Place-Sortierung mit einem .NET String nicht möglich ist - schade.

1voto

scwagner Punkte 3945

Sie brauchen keine 2 char-Arrays zu verwenden. Der Datentyp System.String hat einen eingebauten Indexer nach Position, der das Zeichen ab dieser Position zurückgibt, so dass Sie einfach eine Schleife von 0 bis (String.Length - 1) durchlaufen können. Wenn Sie mehr an der Geschwindigkeit als an der Optimierung des Speicherplatzes interessiert sind, könnten Sie ein HashSet für eine der Zeichenketten erstellen und dann ein zweites HashSet, das Ihr Endergebnis enthält. Dann wird die zweite Zeichenkette durchlaufen, wobei jedes Zeichen mit dem ersten HashSet verglichen wird, und wenn es vorhanden ist, wird es dem zweiten HashSet hinzugefügt. Am Ende haben Sie bereits ein einziges HashSet mit allen Schnittmengen und ersparen sich das Durchlaufen der Hashtabelle auf der Suche nach Zeichen mit einem Wert ungleich Null.

EDIT: Ich habe dies vor all den Kommentaren zu der Frage eingegeben, ob ich überhaupt keine eingebauten Container verwenden möchte

0 Stimmen

Klingt gut..aber wir können immer noch durch die Hashset iterieren wollen, um diese Zeichen zu erhalten und konvertieren es in String.

1voto

Victor Punkte 5567

Ich würde das folgendermaßen machen. Es ist immer noch O(N) und es verwendet keine Hashtabelle, sondern ein int-Array der Länge 26. (idealerweise)

  1. Erstellen Sie ein Array mit 26 Ganzzahlen, wobei jedes Element für einen Buchstaben des Alphabets steht. init to 0's.
  2. iteriert über die erste Zeichenkette und verringert den Wert um eins, wenn ein Buchstabe gefunden wird.
  3. iteriert über die zweite Zeichenkette und nimmt den Absolutwert von dem Index, der einem beliebigen Buchstaben entspricht. (edit: Dank an scwagner in den Kommentaren)
  4. gibt alle Buchstaben zurück, die allen Indizes entsprechen, deren Wert größer als 0 ist.

immer noch O(N) und zusätzlicher Platz von nur 26 Ints.

Wenn Sie nicht nur auf Klein- oder Großbuchstaben beschränkt sind, kann sich die Größe des Feldes natürlich ändern.

0 Stimmen

In Fortsetzung des obigen Beispiels ist c[a] = -2 nach dem ersten Durchlauf, und während des Durchlaufs von s2 wird das Vorzeichen für das erste Auftreten von 'a' umgedreht, und wir werden das Vorzeichen für das zweite Auftreten von 'a' nicht umdrehen, da es bereits +ve........ ist

2 Stimmen

Anstatt das Vorzeichen in Schritt 3 umzudrehen, würde die Verwendung von Math.Abs die Zahl immer positiv halten, selbst bei geraden, sich wiederholenden Zeichenzahlen für s2.

0 Stimmen

Hier anstelle von Array von ints können wir Array von bools verwenden, wobei jeder Index der char ascii Wert ist, so dass wir bool Array der Größe 256 benötigen.

0voto

rmoore Punkte 14842

"wobei jeder Buchstabe höchstens einmal vertreten ist"

Ich nehme an, dass dies bedeutet, dass Sie nur die Schnittpunkte kennen müssen und nicht, wie oft sie vorkommen. Wenn das so ist, können Sie Ihren Algorithmus durch die Verwendung von Ertrag . Anstatt die Zählung zu speichern und die zweite Zeichenkette auf der Suche nach weiteren Übereinstimmungen weiter zu iterieren, können Sie die Schnittmenge gleich hier ermitteln und mit der nächsten möglichen Übereinstimmung der ersten Zeichenkette fortfahren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X