DEV Community

Quick C# Challenges to Keep Your Coding Skills Fresh 2."Palindrome"

Το πρόγραμμα PalindromeCheck είναι μια κονσολάτη εφαρμογή σε C#, που ζητάει από τον χρήστη να πληκτρολογήσει ένα κείμενο και του απαντά αν είναι παλίνδρομο (palindrome) ή όχι.

Παλίδρομο είναι μια λέξη ή φράση που διαβάζεται ίδια προς τα εμπρός και προς τα πίσω — π.χ.
“ΑΝΝΑ”, “κακό”, ή στα Αγγλικά “madam”, “racecar”.

Το πρόγραμμα:

Διαβάζει είσοδο από τον χρήστη.

Αν ο χρήστης γράψει exit, το πρόγραμμα σταματά.

Διαφορετικά, καθαρίζει τη συμβολοσειρά (αφαιρεί κενά, σημεία στίξης, πεζοποιεί) και ελέγχει αν είναι παλίνδρομη.


Δημιουργούμε ένα console app:
Program.cs

namespace PalindromeCheck
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("PalindromeCheck - Εισάγετε κείμενο (γράψτε 'exit' για έξοδο):");


            while (true)
            {
                Console.Write("> ");
                string? input = Console.ReadLine();
                if (input == null) break;


                if (input.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine("Έξοδος. Καλή συνέχεια!");
                    break;
                }


                bool isPal = PalindromeService.IsPalindrome(input);
                Console.WriteLine(isPal ? "Είναι palindrome." : "Δεν είναι palindrome.");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

PalindromeService.cs

🔹 1. Η γραμμή:

if (input.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))

Αναλυτικά τι κάνει:

  • input → είναι το κείμενο που έγραψε ο χρήστης.
  • Trim() → αφαιρεί κενά από την αρχή και το τέλος του κειμένου. Π.χ. " exit ".Trim() → "exit".
  • .Equals("exit", StringComparison.OrdinalIgnoreCase) → συγκρίνει το καθαρισμένο κείμενο με τη λέξη "exit".

Η παράμετρος StringComparison.OrdinalIgnoreCase σημαίνει:

Κάνε τη σύγκριση χωρίς διάκριση πεζών/κεφαλαίων, χρησιμοποιώντας τη γρήγορη, binary (Ordinal) μέθοδο.

✅ Δηλαδή, αν ο χρήστης γράψει EXIT, Exit, ExIt ή exit, θα θεωρηθεί το ίδιο και θα γίνει έξοδος από το πρόγραμμα.


using System.Text;

namespace PalindromeCheck
{
    static class PalindromeService
    {
        public static bool IsPalindrome(string s)
        {
            if (string.IsNullOrEmpty(s)) return true;


            var cleaned = CleanString(s);
            int i = 0, j = cleaned.Length - 1;
            while (i < j)
            {
                if (cleaned[i] != cleaned[j]) return false;
                i++; j--;
            }
            return true;
        }


        private static string CleanString(string input)
        {
           StringBuilder sb = new StringBuilder(input.Length);
            foreach (char c in input)
            {
                if (char.IsLetterOrDigit(c))
                    sb.Append(char.ToLowerInvariant(c));
            }
            return sb.ToString();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🔹 2. Το κομμάτι:

var cleaned = CleanString(s);
int i = 0, j = cleaned.Length - 1;
while (i < j)
{
    if (cleaned[i] != cleaned[j]) return false;
    i++; j--;
}
return true;

Enter fullscreen mode Exit fullscreen mode

Τι κάνει συνολικά:

Αυτό είναι ο πυρήνας του ελέγχου palindrome.

var cleaned = CleanString(s);
Καλεί μια βοηθητική μέθοδο (θα την εξηγήσουμε μετά) που καθαρίζει τη συμβολοσειρά — αφαιρεί σημεία στίξης και κενά, και τη μετατρέπει σε πεζά.

Π.χ.
"A man, a plan, a canal: Panama" → "amanaplanacanalpanama"

int i = 0, j = cleaned.Length - 1;
Δημιουργεί δύο δείκτες:

i ξεκινά από την αρχή της συμβολοσειράς,

j από το τέλος.

while (i < j)
Όσο ο δείκτης i είναι πριν από τον j, συνεχίζουμε να συγκρίνουμε.

if (cleaned[i] != cleaned[j]) return false;
Αν βρεθεί ένα μόνο ζευγάρι χαρακτήρων που δεν είναι ίδιο (π.χ. ‘a’ ≠ ‘b’), τότε δεν είναι palindrome — οπότε σταματά αμέσως και επιστρέφει false.

i++; j--;
Αν οι χαρακτήρες ταιριάζουν, προχωράμε προς το κέντρο — αυξάνουμε i, μειώνουμε j.

Όταν ο βρόχος τελειώσει (δηλαδή συγκρίθηκαν όλα τα ζεύγη χωρίς διαφορά),
→ return true; — σημαίνει ότι το κείμενο είναι palindrome.


🔹 3. Το κομμάτι:

private static string CleanString(string input)
{
    StringBuilder sb = new StringBuilder(input.Length);
    foreach (char c in input)
    {
        if (char.IsLetterOrDigit(c))
            sb.Append(char.ToLowerInvariant(c));
    }
    return sb.ToString();
}
Enter fullscreen mode Exit fullscreen mode

Αναλυτική εξήγηση:

Αυτή η μέθοδος καθαρίζει τη συμβολοσειρά πριν τον έλεγχο — είναι η “προεπεξεργασία”.

StringBuilder sb = new StringBuilder(input.Length);
Δημιουργεί ένα “δοχείο” για να χτίσουμε σταδιακά τη νέα συμβολοσειρά.
Είναι πιο αποδοτικό από την απλή σύνθεση string με +.

foreach (char c in input)
Περνάει κάθε χαρακτήρα του αρχικού κειμένου, έναν-έναν.

if (char.IsLetterOrDigit(c))
Ελέγχει αν ο χαρακτήρας είναι γράμμα ή αριθμός.
➜ Αγνοεί κενά, κόμματα, τελείες, θαυμαστικά κ.λπ.

sb.Append(char.ToLowerInvariant(c));
Αν είναι αποδεκτός χαρακτήρας, τον μετατρέπει σε πεζό (ToLowerInvariant) και τον προσθέτει στο StringBuilder.

Invariant σημαίνει ότι η μετατροπή δεν εξαρτάται από το σύστημα ή το locale.
Π.χ. σε τουρκικά ή ελληνικά locales μπορεί να διαφέρουν τα αποτελέσματα αν δεν το χρησιμοποιήσεις.

return sb.ToString();
Μετατρέπει το StringBuilder πίσω σε κανονικό string και το επιστρέφει.


Ο StringBuilder είναι μια από τις πιο χρήσιμες κλάσεις της C# όταν δουλεύεις με πολλά string, γιατί λύνει ένα βασικό πρόβλημα:
τα string στην C# είναι immutable (δηλαδή δεν αλλάζουν ποτέ αφού δημιουργηθούν).

💡 Τι σημαίνει ότι τα string είναι immutable

Όταν γράφεις κάτι σαν:

string s = "Hello";
s += " world!";
Enter fullscreen mode Exit fullscreen mode

στην πραγματικότητα δεν τροποποιείται η ίδια μεταβλητή s.
Η C# δημιουργεί ένα νέο string στη μνήμη που περιέχει "Hello world!", και η παλιά "Hello" μένει εκεί μέχρι να την καθαρίσει το garbage collector.

Αυτό δεν είναι πρόβλημα για λίγες πράξεις,
αλλά αν έχεις χιλιάδες επαναλήψεις (π.χ. for loop που κάνει += κάθε φορά),
τότε δημιουργούνται χιλιάδες προσωρινά string αντικείμενα,
που επιβαρύνουν τη μνήμη και επιβραδύνουν την εφαρμογή.


🧱 Εδώ μπαίνει ο StringBuilder

Η κλάση System.Text.StringBuilder επιτρέπει να χτίζεις strings “κομμάτι-κομμάτι”,
χωρίς να δημιουργείται νέο αντικείμενο κάθε φορά.

Παράδειγμα:

StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(" ");
sb.Append("world!");
string result = sb.ToString();
Enter fullscreen mode Exit fullscreen mode

➡️ Εδώ ο StringBuilder διατηρεί ένα εσωτερικό buffer (πίνακα χαρακτήρων).
Κάθε Append() προσθέτει χαρακτήρες σε αυτόν τον buffer.
Όταν τελειώσεις, το ToString() παράγει το τελικό string μία και μόνο φορά.


⚙️ Πώς δουλεύει εσωτερικά

Μπορείς να το φανταστείς σαν “δυναμικό πίνακα χαρακτήρων”:

  • Ο StringBuilder ξεκινά με έναν εσωτερικό πίνακα (π.χ. 16 χαρακτήρες).
  • Όταν γεμίσει, μεγαλώνει αυτόματα (συνήθως διπλασιάζει τη χωρητικότητα).
  • Δεν ξαναδημιουργεί ολόκληρη τη συμβολοσειρά κάθε φορά — απλώς γράφει χαρακτήρες στο τέλος του buffer.
  • Όταν καλέσεις ToString(), φτιάχνει ένα κανονικό string από το περιεχόμενο.

Αυτό κάνει τη διαδικασία πολύ πιο γρήγορη και αποδοτική όταν δουλεύεις με πολλές προσθήκες, ειδικά σε loops.


🧮 Παράδειγμα Απόδοσης

Χωρίς StringBuilder:

string text = "";
for (int i = 0; i < 10000; i++)
{
    text += i.ToString() + " ";
}
Enter fullscreen mode Exit fullscreen mode

→ Δημιουργεί 10.000 νέα string αντικείμενα!
Καταναλώνει πολλή μνήμη και χρόνο.

Με StringBuilder:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    sb.Append(i).Append(" ");
}
string text = sb.ToString();
Enter fullscreen mode Exit fullscreen mode

→ Δημιουργεί μόνο ένα StringBuilder και ένα τελικό string στο τέλος.
→ Πολύ πιο αποδοτικό.


🧰 Κύριες Μέθοδοι του StringBuilder

Μέθοδος Περιγραφή
Append(string) Προσθέτει string στο τέλος.
AppendLine(string) Προσθέτει string και νέα γραμμή (\\n).
Insert(int index, string value) Εισάγει string σε συγκεκριμένη θέση.
Remove(int start, int length) Αφαιρεί χαρακτήρες από το buffer.
Replace(string oldValue, string newValue) Αντικαθιστά κείμενο μέσα στο buffer.
Clear() Καθαρίζει το περιεχόμενο (χωρίς να δημιουργεί νέο αντικείμενο).
ToString() Επιστρέφει το τελικό string.

🧠 Παράδειγμα Χρήσης σε PalindromeCheck
Στο πρόγραμμά σου:

StringBuilder sb = new StringBuilder(input.Length);
foreach (char c in input)
{
    if (char.IsLetterOrDigit(c))
        sb.Append(char.ToLowerInvariant(c));
}
return sb.ToString();
Enter fullscreen mode Exit fullscreen mode

Εδώ:

  • Δημιουργείς έναν StringBuilder με αρχικό μέγεθος όσο το μήκος του input.
  • ➜ Έτσι αποφεύγεις επαναλαμβανόμενες αυξήσεις του buffer.
  • Προσθέτεις μόνο τους επιτρεπτούς χαρακτήρες (Append).
  • Τους πεζοποιείς με ToLowerInvariant.
  • Τελικά, με ToString() παίρνεις το “καθαρισμένο” κείμενο.

Έτσι αποφεύγεις να δημιουργείς ένα νέο string σε κάθε επανάληψη του foreach.
Είναι πολύ πιο αποδοτικό και σωστός τρόπος για τέτοια επεξεργασία.


🧭** Πότε να χρησιμοποιείς (και πότε όχι) StringBuilder**

Χρησιμοποίησε StringBuilder όταν:

  • Χτίζεις ένα string σταδιακά (π.χ. σε loop).
  • Κάνεις πολλές “συναρμολογήσεις” string.
  • Επεξεργάζεσαι μεγάλο κείμενο (όπως logs, αρχεία, HTML, SQL queries κ.λπ.).

🚫 Δεν χρειάζεται αν:

  • Ενώνεις λίγα string (2–3 κομμάτια).
  • Κάνεις string interpolation (π.χ. $\"Hello {name}\").

*Συνοψίζοντας *

  • StringBuilder = ένας mutable builder για strings.
  • Προσφέρει ταχύτητα και χαμηλή κατανάλωση μνήμης.
  • Χρησιμοποιεί εσωτερικό buffer αντί να δημιουργεί νέα string.
  • Τελικό αποτέλεσμα δίνεται με .ToString().

1."ReverseString"

Top comments (0)