Angella Andrea - Italian Blog

Infinita passione per lo sviluppo software !
posts - 129, comments - 318, trackbacks - 7

My Links

News

MIT OpenCourseWare: I'm invested Wikipedia Affiliate Button


Sto leggendo:

Archives

Post Categories

Siti web realizzati

Siti web tecnici

Algoritmi – Dire se due parole sono anagrammi


Problema:

Date due parole (stringhe) dire se sono anagrammi.

Input:

Due stringhe che rappresentano due parole (spazi esclusi)

Output:

Un booleano

Esempio:

Input: “nave” e “vena”
Output: true

Input: “nave” e “neve”
Output: false

Il mio ragionamento:

Se le due parole hanno un numero di caratteri differenti non possono essere anagrammi.

Calcolo il numero di occorrenze di ciascun carattere nella prima parola.

Per ogni carattere della seconda parola verifico se ce n’e’ uno corrispondente nella prima decrementando il contatore corrispondente. Se uno dei contatori e’ pari a zero prima del decremento significa che non c’e’ un numero sufficiente di caratteri e quindi ritorno false.

Nota: contare se un qualche contatore e’ rimasto maggiore di zero dopo l’ultimo foreach non e’ necessario perche’ sappiamo che le due parole hanno la stessa lunghezza. Se il numero di occorrenze di una certa lettera nella prima parola e’ maggiore rispetto al numero di occorrenze nella seconda parola, allora esistera’ sempre la situazione opposta.

La mia soluzione O(N):

static bool IsAnagram(string s1, string s2)
{
    if (s1 == null) throw new ArgumentNullException("s1");
    if (s2 == null) throw new ArgumentNullException("s2");
    
    if (s1.Length != s2.Length)
        return false;

    uint[] counters = new uint[char.MaxValue + 1];

    foreach(char c in s1)
        ++counters[c];

    foreach(char c in s2)
        if( counters[c]-- == 0) return false;

    return true;
}


Mia seconda soluzione utilizzando l’idea dell’ordinamento O(N):

static class StringExtensions
{
    public static char[] ToOrderedCharArray(this string s)
    {
        if (s == null) throw new ArgumentNullException("s");

        char[] chars = new char[s.Length];
        
        uint[] counters = new uint[char.MaxValue + 1];
        foreach (char c in s) ++counters[c];

        int p = 0;

        for(int c = 0; c < counters.Length; ++c)
            for (int i = 0; i < counters[c]; ++i)
                chars[p++] = (char)c;

        return chars;
    }
}

static class ArrayExtensions
{
    public static bool IsEquivalent<T>(this T[] array1, T[] array2)
    {
        if (array2 == null) throw new ArgumentNullException("vc2");
        if (array1.Length != array2.Length) return false;
        
        int n = array1.Length;

        for(int i = 0; i < n; i++)
            if( !array1[i].Equals(array2[i]) )
                return false;

        return true;
    }
}

static bool IsAnagram_Sorting_Version(string s1, string s2)
{
    if (s1 == null) throw new ArgumentNullException("s1");
    if (s2 == null) throw new ArgumentNullException("s2");

    if (s1.Length != s2.Length) return false;

    char[] vc1 = s1.ToOrderedCharArray();
    char[] vc2 = s2.ToOrderedCharArray();

    return vc1.IsEquivalent(vc2);
}


La leggibilita’ di questa soluzione e’ indiscutibile.

La complessita’ e’ ancora O(N) anche se la performance e’ piu’ bassa rispetto alla soluzione precedente.
Inoltre questa soluzione richiede un uso maggiore della memoria per memorizzare gli array ordinati di caratteri.

Personalmente ritengo la versione precedente preferibile.

Print | posted on sabato 4 settembre 2010 16.48 | Filed Under [ Algoritmica ]

Feedback

Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

Ordinare i caratteri della stringa come fosse un array e verificare se step dopo step i caratteri dei due array sono uguali?
Ovviamente dopo aver controllato la lunghezza come dicevi
04/09/2010 19.50 | Roberto
Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

Ciao Roberto, si la tua e' un'altra soluzione:

sort(s1) == sort(s2)

Tuttavia la complessita' diventerebbe O(N * log(N)) invece che O(N).
04/09/2010 21.33 | Andrea Angella
Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

Nel codice presentato il true indica solo che la prima parola è contenuta nella seconda, manca la verifica che l'array counter abbia somma interna uguale a 0.
04/09/2010 22.36 | Marco
Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

Nel codice iniziale che avevo scritto avevo fatto una verifica di questo tipo prima del return true:

foreach(char c in s1)
if( counters[c] > 0 )
return false

Tuttavia riflettendoci bene, come ho scritto nella sezione "Il mio ragionamento" tale verifica e' superflua. Se arrivo a quel punto sono certo che la somma sia zero.

Riporto il mio commento:
Nota: contare se un qualche contatore e’ rimasto maggiore di zero dopo l’ultimo foreach non e’ necessario perche’ sappiamo che le due parole hanno la stessa lunghezza. Se il numero di occorrenze di una certa lettera nella prima parola e’ maggiore rispetto al numero di occorrenze nella seconda parola, allora esistera’ sempre la situazione opposta. Aggiungo: cioe' porterebbe a ritornare false.

Dimostrarlo matematicamente e' difficile, ma la dimostrazione dovrebbe essere un ragionamento per assurdo:

Supponiamo per assurdo che prima di "return true" counters contenga un elemento in posizione c maggiore di zero. Questo significhebbe che la prima parola avrebbe un numero maggiore di caratteri c rispetto alla seconda. Nella seconda stringa allora al posto di tali caratteri mancanti ce ne dovrebbero essere degli altri. Supponiamo che uno di questi altri sia il carattere d. Se d e' presente nella prima stringa perforza si avrebbe che il numero di d nella seconda stringa e' maggiore al numero di d nella prima, ma questo porterebbe a ritornare false nel precedente ciclo in quanto il contatore raggiungerebbe zero. Se d non appartiene alla prima stringa allo stesso modo il contatore sarebbe gia' a zero e il precedente ciclo tornerebbe false. Quindi la nostra ipotesi non e' valida e tutti gli elementi di counters devono essere pari a zero. Nota: non e' possibile che siano negativi, altrimenti il precedente ciclo ritornerebbe false.

Lo so un casino, ma penso che sia un ragionamento corretto.

Se non sei convinto, vedi se riesci a trovare un controesempio per cui il mio algoritmo fallisce e postalo.

Saluti
04/09/2010 23.49 | Andrea Angella
Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

Ciao Andrea,

se assumiamo di poter allocare memoria extra come nella tua soluzione con un array di contatori, allora anche l'algoritmo di ordinamento può diventare O(N) :-)

Secondo me, la soluzione di Roberto basata sul sort è molto buona perché è semplice e si capisce subito come funziona...
06/09/2010 14.47 | xpmatteo
Gravatar

# re: Algoritmi – Dire se due parole sono anagrammi

@Matteo

Si hai ragione, utilizzando il vettore counters e' possibile rendere l'ordinamento di complessita' O(N). La soluzione finale diventerebbe sicuramente piu' leggibile ma sarebbe meno efficiente.

Ho provato ad implementarla e ho aggiunto il codice al post.

Grazie per l'interessante osservazione.
06/09/2010 21.14 | Andrea Angella
Comments have been closed on this topic.

Powered by: