Usuwanie duplikatów wyrazów z listy.

0

Witam.

Mam problem z napisaniem pewnego programu. Napisany poniżej program usuwa mi duplikaty podanych liczb z klawiatury i ok wszystko działa. Tylko jak zrobić, żeby zamiast wpisywanych liczb usuwał mi duplikaty wyrazów z pliku txt np.:

Plik źródłowy txt:

marek
marta
marek
jan

Plik txt wynikowy:

marek
marta
jan

Będę bardzo wdzięczny za pomoc w rozwiązaniu tego problemu.
Dopiero się uczę programować dlatego bardzo proszę o wyrozumiałość.

using namespace std;

int main()
{
   long int tablica[100], duplikaty[100];
   int n,e=0;
   cout << "Ile liczb? ";
   cin >> n;
   int ile=0;
   for(int i=0; i<n; i++)
   {
      cout << "Podaj liczbe numer " << i+1 << ": ";
      cin >> e;
      tablica[i]=e;
   }
   cout << endl << "Tablica przed usunieciem duplikatow: ";
   for(int j=0; j<n; j++)
   {
      cout << tablica[j] << ",";
   }
   cout << endl;
   for(int k=0; k<n; k++)
   {
      for(int l=0; l<n; l++)
      {
         if((tablica[k]==tablica[l]) && (l<=n) && (k!=l))
         {
            if(tablica[k]==tablica[l])
            {
               duplikaty[k]=0;
            }
         }
      }
   }
   cout << endl << "Tablica po usunieciu duplikatow: ";
   for(int o=0; o<n; o++)
   {
      if(duplikaty[o]!=0)
      cout << tablica[o] << ",";
   }
   cout << endl;
   system("pause");
   return 0;
}
1

Z związku że uczysz się programować,żeby Ci pomóc chce wiedzieć czy znasz takie rzeczy jak strumienie plikowe(np ifstream),kontenery ( np. vector) i algorytmy ogólne z stla ?

1

Proszę bardzo. Wadą tego programiku jest to że elementy na wyjściu są posortowane

#include <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
     string nazwaPliku;
     cout << "Podaj nazwe pliku do otwarcia: ";
     cin >> nazwaPliku;
     ifstream plikwe(nazwaPliku.c_str());
     if(!plikwe)
     {
          cerr << "Blad otwarcia pliku\n";
          return -1;
     }

     string tmp;
     vector<string> tekst;
     while(plikwe >> tmp)
     {
          tekst.push_back(tmp);
     }

     cout << "Przed usunieciem duplikatow\n";
     for(int i = 0; i < tekst.size(); ++i)
     {
          cout << tekst[i] << "\n";
     }
     // najpierw sortujemy slowa, bo algorytm unique "usuwa" tylko
     // slowa lezace obok siebie
     sort(tekst.begin(),tekst.end());
     // algorytm unique przenosi duplikaty na koniec, i zwraca nowy logiczny
     // koniec. Aby usunac calkowicie z kontenera duplikaty
     // stosujemy metode erase.
     tekst.erase(unique(tekst.begin(),tekst.end()),tekst.end());

     cout << "Po usunieciu duplikatow\n";
     for(int i = 0; i < tekst.size(); ++i)
     {
          cout << tekst[i] << "\n";
     }

     cout << "Podaj nazwe pliku do ktorego zapisac\n";
     cin >> nazwaPliku;
     ofstream plikwy(nazwaPliku.c_str());

     for(int i = 0; i < tekst.size(); ++i)
     {
          plikwy << tekst[i] << "\n";
     }


}

0

WOW :-) Super dzięki. Mam jeszcze ostatnie pytanie. Czy da się zrobić tak by np.usuwało przeciwieństwa np.:

Źródło:
marek | kasia
kasia | marek
kinga |jarek
marek | asia

Wynik:

kinga |jarek
marek | asia

Jeszcze raz bardzo Ci dziękuję za pomoc.

0

Nie rozumiem o co Ci chodzi z tymi przeciwieństwami.

0

Chodzi o to, że jak będę miał jakieś formuły w tekście np.:

W pliku mam:

-p | q
-q | p
-a | b

To usunie te formuły i zostawi:

|-oddzielam wyrazy

-a | b

0

Wczytaj sobie pary wyrazów np. do struktury std::pair<string, string>.

typedef std::pair<string, string> Element;
vector<Element> lista;
/* --> tutaj wczytywanie <--- */

Zdefiniuj sobie funkcję porównującą elementy wg. ustalonych kryteriów (z tego co zrozumiałem kolejność słów w parze ma być ignorowana)

bool CzyElementMniejszy(const Element &a, const Element &b)
{
    const string *a_mniejsze = &a->first;
    const string *a_wieksze = &a->second;
    if (*a_mniejsze > *a_wieksze) Swap(a_mniejsze, a_wieksze);

    const string *b_mniejsze = &b->first;
    const string *b_wieksze = &b->second;
    if (*b_mniejsze > *b_wieksze) Swap(b_mniejsze, b_wieksze);

    int cmp_mniejsze = a_mniejsze->compare(*b_mniejsze);
    return cmp_mniejsze < 0 || (cmp_mniejsze == 0 && *a_wieksze < *b_wieksze);
}

bool CzyElementRowny(const Element &a, const Element &b)
{
    return (a.first == b.first && a.second == b.second) || (a.first == b.second && a.second == b.first);
}

oraz użyj tych funkcji podczas sortowania/usuwania duplikatów:

sort(lista.begin(),lista.end(), CzyElementMniejszy);
lista.erase(unique(lista.begin(),lista.end(), CzyElementRowny),lista.end());
0

Trochę niejasno napisałem. Chodzi mi oto by:

-oddziela formuły z lewej strony od tych z prawej

p#d,s
p,q#s
p,s#q

Nie wiem jak zrobić by program brał pierwszą formułę (pierwsza linia w notatniku) i porównywał z każdą następną tzn.

Bierze: p#d,s

Sprawdza z pozostałymi i jak znajdzie w jakiejś formule, że ta sama litera wystąpi po lewej i po prawej stronie znaku # to je kasuje i powstają nowe formuły:

p#d,s
p,s#q

s – występuje po lewej i po prawej stronie więc jest usuwana i powstają nowe formuły:

p#d
p#q

Program ma tak długo działać aż wymaże wszystkie formuły i wtedy zwróci prawdę a w przypadku gdy zostanie formuła, której nie można z niczym zredukować to zwróci fałsz.

Jedna ze stron może być pusta np.

p#
#p

Wtedy po prostu je kasuje i nie powstaje żadna formuła.

Z góry bardzo dziękuję za pomoc, bo nie umiem dać sobie z tym rady.

0

Można użyć brute-force czyli bierzemy po kolei każdy element i sprawdzamy z każdym kolejnym elementem czy mają wspólne znaki po przeciwnych stronach, jeśli mają to usunąć

(niesprawdzone)

struct Formula
{
    string lewa;
    string prawa;
};

vector<Formula> lista;

void UsunPowtorzenia(string &str1, string &str2)
{
    for (int i1 = 0; i1 < str1->size(); ) {
        int i2 = str2->find(str1[i1]);

        if (i2 != string::npos) {
            str1.erase(i1, 1);
            str2.erase(i2, 1);
        } else {
            i1++;
        }
    }
}


for (vector<Formula>::iterator a = lista.begin(); a < lista.end() - 1; b++)
{
    for (vector<Formula>::iterator b = a + 1; b < lista.end(); b++)
    {
        UsunPowtorzenia(a->lewy, b->prawy);
        UsunPowtorzenia(a->prawy, b->lewy);
    }
}

Być może sposób wystarczający. Jeśli jednak wydajność jest za słaba to można coś pokombinować ale musiałbyś opisać dokładniej owe tajemnicze formuły - ile ich będzie, jak długie będą formuły, z jakich elementów się będą składać ... właściwie każda cecha charakterystyczna danych może mieć znaczenie.

I jeszcze jedna wątpliwość - co zrobić, gdy możliwości redukowania będzie więcej niż jedna? Wybrać dowolną?
Dla przykładu
p#q p#r s#p
ma zostać:

  1. #q p#r s#
  2. p#q #r s#
  3. 1 lub 2, obojętne
  4. #q #r s#
  5. jeszcze inaczej
    ?
0

Te z-formuły będę wczytywał z notatnika, będzie ich ok. kilkanaście. Biorą się one ze zdań z logiki np. (~p+q+s) i (~p+~q+s) i (~p+q+~s) i wtedy otrzymam takie z-formuły:

p#q,s
p,q#s
p,s#q

I tego typu formuły wrzucam do programu.

" I jeszcze jedna wątpliwość - co zrobić, gdy możliwości redukowania będzie więcej niż jedna? Wybrać dowolną?"
Dla przykładu
p#q
p#r
s#p
ma zostać:

  1. #q p#r s#

I. Biorę formułę p#q sprawadzam wszystkie wczytane formuły (porównuję pierwszą z każdą wczytaną) czy p występuje też z prawej strony.
II. Okazuje się, że występuje w formule trzeciej: s#p, więc usuwam powtórzenie i zostaje:

#q
p#r
s#

III. Z pierwszej formuły biorę q i sprawdzam czy jest, w którejś z wczytanych formuł jeżeli nie to zostawiam itd. Biorę drugą formułę p#r, biorę p i dalej sprawdzam czy jest taka formuła z p po prawej stronie potem biorę literę r i sprawdzam czy znajduje się po lewej stronie, którejś z formuł. Następnie biorę trzecią formułę s# i sprawdzam czy s znajduje się po prawej stronie jeżeli nie to zostawiam i w tym przypadku zwracam fałsz bo nie wszystkie składniki się skasowały.

Mogą być np. takie formuły p,q,s#w,z

Wiem, że jest to trochę zakręcone. Dzięki za pomoc bo sam niestety nie mogę sobie dać z tym rady.

1 użytkowników online, w tym zalogowanych: 0, gości: 1