C# Coding Test Course, String Search

Problem Description

You need to find out how many times a specific string P appears in the string S. String P can appear multiple times in string S, and there may be overlapping occurrences. The length of the given string S is between 1 and 100,000, and the length of string P is between 1 and 100. The comparison is case-insensitive.

Input Format

  • First line: string S (1 ≤ |S| ≤ 100,000)
  • Second line: string P (1 ≤ |P| ≤ 100)

Output Format

Print the total number of occurrences as an integer.

Example

Input

    abCabcABCabc
    abc
    

Output

    4
    

Problem Solving Process

1. Understanding the Problem

This problem involves checking how many times string P appears in the given string S. Since string comparison is case-insensitive, both strings should be converted to lowercase for comparison.

2. Approach

There are several approaches to string search problems, but we will solve it directly using a simple loop. The following steps will be taken to solve the problem:

  1. Convert string S to lowercase.
  2. Convert string P to lowercase.
  3. Use a loop to find string P in string S.
  4. Increment the count for each occurrence.

3. Algorithm Design

The complexity of the algorithm is O(n*m), where n is the length of string S and m is the length of string P. Since we are directly comparing the two strings, in the worst case we may need to search string P from every index.

4. C# Code Implementation

Below is an example of C# code.

    using System;

    class Program
    {
        static void Main(string[] args)
        {
            string S = Console.ReadLine();
            string P = Console.ReadLine();

            // Convert to lowercase for case-insensitive comparison
            S = S.ToLower();
            P = P.ToLower();

            int count = 0;
            int position = 0;

            while ((position = S.IndexOf(P, position)) != -1)
            {
                count++;
                position++; // Move position by one to consider overlapping cases
            }

            Console.WriteLine(count);
        }
    }
    

5. Code Explanation

The code includes the following steps:

  1. Accept input strings S and P from the user.
  2. Convert strings S and P to lowercase for easier comparison.
  3. Use a while loop to find the position of P in string S.
  4. Use the S.IndexOf() method to locate P starting from the current position. If the found position is not -1, increase the count and move to the next position.
  5. Output the total number of occurrences.

Performance Considerations

The time complexity of this code is O(n*m), which varies depending on the lengths of strings S and P. If the length of S is 100,000 and P is 100, the worst case could require 10,000,000 operations. This may be somewhat inefficient.

Therefore, if you wish to further improve performance, you might consider using string search algorithms like KMP (Knuth-Morris-Pratt). The KMP algorithm has a time complexity of O(n + m) and enables more efficient searching.

5-1. Overview of the KMP Algorithm

The KMP algorithm is an efficient method for substring searching. It operates on the following principles:

  • First, it creates an array to store the partial matches of the pattern string P.
  • While scanning string S, it calculates how many characters can be skipped in the pattern string when a mismatch occurs.

5-2. KMP Algorithm C# Implementation

Below is the C# code implementing the KMP algorithm.

    using System;

    class Program
    {
        static void Main(string[] args)
        {
            string S = Console.ReadLine();
            string P = Console.ReadLine();

            // Convert to lowercase for case-insensitive comparison
            S = S.ToLower();
            P = P.ToLower();

            int count = KMP(S, P);
            Console.WriteLine(count);
        }

        static int KMP(string S, string P)
        {
            int m = P.Length;
            int n = S.Length;
            int count = 0;

            // Initialize LPS array
            int[] lps = new int[m];
            ComputeLPSArray(P, m, lps);

            int i = 0; // Index for S
            int j = 0; // Index for P

            while (i < n)
            {
                if (P[j] == S[i])
                {
                    i++;
                    j++;
                }

                if (j == m)
                {
                    count++;
                    j = lps[j - 1];
                }
                else if (i < n && P[j] != S[i])
                {
                    if (j != 0)
                        j = lps[j - 1];
                    else
                        i++;
                }
            }
            return count;
        }

        static void ComputeLPSArray(string P, int m, int[] lps)
        {
            int len = 0;
            int i = 1;
            lps[0] = 0;

            while (i < m)
            {
                if (P[i] == P[len])
                {
                    len++;
                    lps[i] = len;
                    i++;
                }
                else
                {
                    if (len != 0)
                        len = lps[len - 1];
                    else
                    {
                        lps[i] = 0;
                        i++;
                    }
                }
            }
        }
    }
    

6. KMP Algorithm Code Explanation

The above code operates in the following manner:

  1. First, it converts strings S and P to lowercase to eliminate case sensitivity.
  2. Calls the KMP method to explore how many times string P appears in string S.
  3. Inside the KMP method, it generates the LPS array. The LPS array stores the maximum length of the prefix and suffix of pattern P.
  4. While scanning string S, it matches the pattern P. If matching is successful, it increments the count, and if matching fails, it adjusts the position based on the LPS array.

Conclusion

In this lecture, we learned how to solve the problem of finding a specific substring in a string using C#. From a simple loop approach to the extension using the KMP algorithm, we gained an understanding of the fundamental concepts of string searching and efficient approaches. I hope this process helped you understand various coding implementations and the complexities of algorithms.

References

C# Coding Test Course, 2 N Tile Filling

Hello! Today, we will explore a detailed problem description and solution process for the 2*N tile filling problem for everyone preparing for a C# coding test. This problem is closely related to Dynamic Programming and is very useful for developing problem-solving skills.

Problem Description

We have a rectangular grid of size 2*N. Our goal is to calculate the number of ways to fill this rectangle using 2*1 size tiles. We can rotate the tiles, so each tile can be placed either vertically or horizontally.

Input

  • Integer N (1 ≤ N ≤ 30) – the width of the rectangle.

Output

  • Print the number of ways to fill the 2*N rectangle with tiles.

Understanding the Problem

To solve this problem, we need to consider several approaches. The number of ways to fill the rectangle with tiles can be thought of as follows:

1. Establishing the Recurrence Relation

The ways to fill a 2*N rectangle can be divided into two cases:

  1. Using 2 tiles horizontally: In this case, the size of the remaining rectangle is 2*(N-1).
  2. Using 1 tile vertically: In this case, the size of the remaining rectangle is 2*(N-2).

Therefore, this can be expressed mathematically as:

    F(N) = F(N-1) + F(N-2)
    

Here, F(N) represents the number of ways to fill a rectangle of size N.

2. Setting Initial Conditions

The initial conditions are as follows:

  • F(1) = 1 (A rectangle of size 2*1 can be filled with either a 2*1 tile or a 1*2 tile.)
  • F(2) = 2 (A rectangle of size 2*2 can be filled with either 2 horizontal tiles or 2 vertical tiles.)

Implementing C# Code

Now, based on the recurrence relation that we established above, let’s implement the algorithm using C#.

    
    using System;

    class Program
    {
        static void Main()
        {
            int N = int.Parse(Console.ReadLine());
            Console.WriteLine(TileFilling(N));
        }

        static int TileFilling(int N)
        {
            if (N == 1) return 1;
            if (N == 2) return 2;

            int[] dp = new int[N + 1];
            dp[1] = 1;
            dp[2] = 2;

            for (int i = 3; i <= N; i++)
            {
                dp[i] = dp[i - 1] + dp[i - 2];
            }

            return dp[N];
        }
    }
    
    

Code Explanation

In the above code, we use a dynamic programming array called dp to store the number of ways to fill the tiles up to N. After setting the cases for lengths 1 and 2 through initial conditions, we perform calculations based on the recurrence relation with a loop.

Main Functions Explanation

  • Main(): The starting point of the program that receives the N value and outputs the number of ways to fill the tiles.
  • TileFilling(int N): Calculates and returns the number of ways to fill the tiles for a given N.

Complexity Analysis

The time complexity of this algorithm is O(N), and the space complexity is O(N). This means that the execution time increases in proportion to N, and it allows for efficient memory usage by storing previous results in an array.

Test Cases

Let's check if the algorithm works well for various N values.

  • Input: N = 1 → Output: 1
  • Input: N = 2 → Output: 2
  • Input: N = 3 → Output: 3
  • Input: N = 4 → Output: 5
  • Input: N = 5 → Output: 8
  • Input: N = 6 → Output: 13

Conclusion

Today, we learned how to solve the 2*N tile filling problem using C#. This problem is a typical example that can be solved using dynamic programming and will greatly help in developing algorithmic thinking. I hope this process of solving the problem has been very helpful in your coding test preparation.

Next time, I will bring another interesting algorithm problem. If you have a topic you would like to see, please let me know in the comments! Thank you.

C# Coding Test Course, I Don’t Want to Be a Liar

Problem Description

Starting from a certain location on a street and during the journey to a specific point, one encounters various people and hears different stories from them.
These individuals can either tell the truth or lie, and we must determine the truth based on what everyone is saying.
Implement an algorithm to determine your position based on the given data.

Problem Definition

Among N people, each person can either tell the truth or lie about what they know.
Each person should be able to determine their truthfulness based on the information from others.
Based on the given conversation information, calculate the number of possible individuals who can speak the truth at a specific point.

Input Format

  • The first line contains the number of people, N.
  • The second line contains conversation information between people. Each piece of information is in the format “A B T/F”, where A is the information provider, B is the information receiver, T signifies truth, and F signifies a lie.

Output Format

Output the total number of people who can speak the truth.

Problem Solving Course

1. Understanding the Problem

To understand the problem, let’s analyze the meaning of the provided data.
People communicate with each other, and we need to establish clear rules regarding whether the information from A to B is true or false.
In such cases, we can represent this information in a graph and use algorithms like DFS or BFS to determine truthfulness.

2. Algorithm Design

We need to create a graph to represent the relationships of information transmission and consider all possible combinations of truths and lies that can occur in conversations.
Using the DFS (Depth First Search) algorithm, the process of propagating truths and lies through each person is as follows.

3. C# Code Implementation

        
        using System;
        using System.Collections.Generic;

        class Program
        {
            static void Main(string[] args)
            {
                // Input
                int N = int.Parse(Console.ReadLine());
                List> conversations = new List>();
                string line;
                while ((line = Console.ReadLine()) != null && line.Length > 0)
                {
                    var parts = line.Split(' ');
                    int A = int.Parse(parts[0]);
                    int B = int.Parse(parts[1]);
                    bool isTruth = parts[2].Equals("T");
                    conversations.Add(new Tuple(A, B, isTruth));
                }

                // Graph formation
                Dictionary>> graph = new Dictionary>>();
                foreach (var convo in conversations)
                {
                    if (!graph.ContainsKey(convo.Item1))
                        graph[convo.Item1] = new List>();
                    graph[convo.Item1].Add(new Tuple(convo.Item2, convo.Item3));
                }

                // Perform DFS
                HashSet visited = new HashSet();
                int truthfulCount = 0;

                void DFS(int person)
                {
                    if (visited.Contains(person)) return;
                    visited.Add(person);
                    truthfulCount++;
                    if (graph.ContainsKey(person))
                    {
                        foreach (var neighbor in graph[person])
                        {
                            if (neighbor.Item2) // Case of speaking the truth
                                DFS(neighbor.Item1);
                        }
                    }
                }

                foreach (var person in graph.Keys)
                {
                    if (!visited.Contains(person))
                    {
                        DFS(person);
                    }
                }

                // Output result
                Console.WriteLine(truthfulCount);
            }
        }
        
        

4. Code Explanation

The above C# code illustrates a simple way to solve the problem using the DFS algorithm.
First, it takes the input and constructs a graph based on the conversation information between people.
Then, it executes DFS for each person to calculate the number of individuals who can speak the truth and outputs the result.

5. Time Complexity Analysis

The time complexity of this algorithm is O(V + E).
Here, V represents the number of people, and E represents the number of conversations.
Thus, the time taken to explore all vertices and edges of the graph is generally considered efficient.

6. Code Improvement and Optimization

Additionally, one could consider using BFS instead of DFS for optimization.
Moreover, introducing memoization to store already confirmed results of truths and lies would allow for quicker returns on similar requests in the future.

Conclusion

In this course, we explored how to construct algorithms to solve the given problem using C#.
By utilizing graph algorithms and DFS, we learned the process of calculating the number of individuals who can speak the truth, which equipped us with useful approaches for real coding tests.
I hope this helps improve problem-solving skills by practicing various problem types and enhancing understanding of algorithms.

C# Coding Test Course, Bubble Sort Program 2

This article describes how to implement the bubble sort algorithm in C#. Bubble sort is one of the most basic sorting algorithms and often appears in various algorithm tests. In this post, we will take a detailed look at the principle of bubble sort and examine an example implemented in C#.

What is Bubble Sort?

Bubble sort is a simple sorting algorithm that compares adjacent elements of an array to arrange the order of the two elements. One can visualize the largest (or smallest) element ‘bubbling’ up to the end of the array. This algorithm can be implemented simply, but it is not an efficient sorting algorithm. Its time complexity is O(n^2).

Problem Description

Sort the given integer array in ascending order using the bubble sort algorithm.

Input

- Input: Integer array arr = [64, 34, 25, 12, 22, 11, 90]

Output

- Output: Sorted array [11, 12, 22, 25, 34, 64, 90]

Explanation of the Bubble Sort Algorithm

Bubble sort follows these steps:

  1. Check the length of the array.
  2. Use two nested loops to iterate through all elements of the array.
  3. Compare two adjacent elements and swap them if they are in the wrong order.
  4. After one full comparison, the largest element moves to the end of the array.
  5. Repeat this process N-1 times to sort the array.

Implementing Bubble Sort in C#

Now, let’s implement the above process in C# code.


using System;

class Program
{
    static void Main()
    {
        int[] arr = { 64, 34, 25, 12, 22, 11, 90 };
        Console.WriteLine("Original array:");
        PrintArray(arr);
        
        BubbleSort(arr);
        
        Console.WriteLine("Sorted array:");
        PrintArray(arr);
    }

    static void BubbleSort(int[] arr)
    {
        int n = arr.Length;
        for (int i = 0; i < n - 1; i++)
        {
            for (int j = 0; j < n - i - 1; j++)
            {
                if (arr[j] > arr[j + 1])
                {
                    // Swap arr[j] and arr[j + 1]
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

    static void PrintArray(int[] arr)
    {
        foreach (var item in arr)
        {
            Console.Write(item + " ");
        }
        Console.WriteLine();
    }
}

Explanation of the Code

The above code is an example of implementing bubble sort using a simple C# console application.

  • BubbleSort(int[] arr): A method that sorts the given array. This method uses two nested loops to iterate through the array, comparing and swapping adjacent elements.
  • PrintArray(int[] arr): A helper method that prints the elements of the array, separating each element with a space.

Conclusion

Bubble sort is an easy-to-learn and understand sorting algorithm, but it is advisable to use other algorithms with better performance in practice. However, understanding the concepts of basic sorting algorithms is important in preparing for coding tests.

I hope this article helps with your future coding test preparations. If you have any questions or points for discussion, please leave a comment!

C# Coding Test Course, Representing Sets

1. Problem Description

In this lecture, we will address the problem of representing sets using C#. Sets are collections of elements that do not allow duplicates, and they are useful in certain situations. For example, sets can be effectively used when handling lists of unique user IDs or various product codes.

Problem: Representation of Sets

Given two arrays A and B, implement a function that calculates the union, intersection, and difference of the two arrays. The function is defined in the following format:

public static void CalculateSets(int[] A, int[] B)

2. Solution Process

To solve this problem, it’s essential to understand the definitions of each set operation and how to utilize sets in C#.

2.1 Definition of Sets

  • Union: The set that includes all elements from both sets.
  • Intersection: The set consisting of elements common to both sets.
  • Difference: The set obtained by excluding elements of the second set from the first set.

2.2 C# Set Class

In C#, sets can be easily implemented using the HashSet class. A HashSet does not allow duplicates and can represent an unordered set.

2.3 Function Implementation

Now we will implement the required function CalculateSets. Below is the code to implement each set operation.

using System;
using System.Collections.Generic;

public class SetOperations
{
    public static void CalculateSets(int[] A, int[] B)
    {
        HashSet setA = new HashSet(A);
        HashSet setB = new HashSet(B);

        // Union
        HashSet union = new HashSet(setA);
        union.UnionWith(setB);
        Console.WriteLine("Union: " + string.Join(", ", union));

        // Intersection
        HashSet intersection = new HashSet(setA);
        intersection.IntersectWith(setB);
        Console.WriteLine("Intersection: " + string.Join(", ", intersection));

        // Difference
        HashSet difference = new HashSet(setA);
        difference.ExceptWith(setB);
        Console.WriteLine("Difference: " + string.Join(", ", difference));
    }
}

3. Code Explanation

Let’s explain the above code step by step.

3.1 Creating HashSets

First, we create a HashSet for each of the given arrays A and B. This way, we obtain a set with duplicate elements removed.

HashSet setA = new HashSet(A);
HashSet setB = new HashSet(B);

3.2 Calculating Union

To calculate the union, we first make a copy of setA and then use the UnionWith method to add elements from setB.

HashSet union = new HashSet(setA);
union.UnionWith(setB);
Console.WriteLine("Union: " + string.Join(", ", union));

3.3 Calculating Intersection

The intersection is obtained by calling the IntersectWith method on a copy of setA with setB.

HashSet intersection = new HashSet(setA);
intersection.IntersectWith(setB);
Console.WriteLine("Intersection: " + string.Join(", ", intersection));

3.4 Calculating Difference

The difference is calculated by removing elements of setB using the ExceptWith method.

HashSet difference = new HashSet(setA);
difference.ExceptWith(setB);
Console.WriteLine("Difference: " + string.Join(", ", difference));

4. Full Code

Finally, the full code that includes all content is as follows:

using System;
using System.Collections.Generic;

public class SetOperations
{
    public static void CalculateSets(int[] A, int[] B)
    {
        HashSet setA = new HashSet(A);
        HashSet setB = new HashSet(B);

        // Union
        HashSet union = new HashSet(setA);
        union.UnionWith(setB);
        Console.WriteLine("Union: " + string.Join(", ", union));

        // Intersection
        HashSet intersection = new HashSet(setA);
        intersection.IntersectWith(setB);
        Console.WriteLine("Intersection: " + string.Join(", ", intersection));

        // Difference
        HashSet difference = new HashSet(setA);
        difference.ExceptWith(setB);
        Console.WriteLine("Difference: " + string.Join(", ", difference));
    }
}

class Program
{
    static void Main()
    {
        int[] A = { 1, 2, 3, 4, 5 };
        int[] B = { 4, 5, 6, 7, 8 };
        SetOperations.CalculateSets(A, B);
    }
}

5. Testing and Results

To test the above code, I defined two arrays in the Main method and called the CalculateSets function.

The results for the given arrays A and B are as follows:

Union: 1, 2, 3, 4, 5, 6, 7, 8
Intersection: 4, 5
Difference: 1, 2, 3

6. Conclusion

In this lecture, we learned how to effectively represent sets using C# and perform operations such as union, intersection, and difference. Such set operations are very useful not only for coding tests but also for solving various algorithm problems. Additionally, utilizing the various methods of C#’s HashSet class allows for easy manipulation of sets. It would be beneficial to revisit the concepts and applications of sets as you prepare for future coding tests.