Skip to main content
2 of 10
Update the problem description to make the test case more helpful.
Jianmin Chen
  • 2.5k
  • 2
  • 28
  • 52

The smallest substring algorithm

Problem statement:

Give a string as a "source" string, find the smallest substring of source such that it contains all characters in "search" string which contains the unique characters.

Given a test case for easy to come out your design of the algorithm, the source string contains characters not in search string, the smallest substring contains the character not in the search string as well. For example, search string ['a','b','c'], source string "aefbcgaxy", the shortest string is "bcga".

My analysis of algorithm

It is my favorite algorithm to practice two pointer techniques and also use slide window to reduce the time complexity to almost linear complexity by overloading the count value of unique characters.

For example, search string ['a','b','c'] can map to a C# dictionary with 'a' as key with value 1, 'b' with 1, 'c' with 1. The value of key 'a' can also be used to track how many 'a' are consumed when iterating the search string from left to right. Key 'a' with value 0 means that no 'a' is needed in sliding window, -1 means that the substring in sliding window has extra one 'a'.

Here is my C# code. I have practiced string search algorithm since January 2015. I am still learning how to write an efficient solution in terms of time complexity, also apply the principle I learned a few months ago called TED Principle (Terse, Express the intent, Do one thing) through pluralsight.com course clean code: write code for humans. Please help me review my code and help me to write clean code.

    using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace substring_practice
{

    class Program
    {
        static void Main(string[] args)
        {
            var testResult = GetShortestUniqueSubstring(new char[] { 'A', 'B', 'C' }, "ADOBECBANCD");
            Debug.Assert(testResult.CompareTo("BANC") == 0);
        }
              
        public static string GetShortestUniqueSubstring(char[] search, string source)
        {
            // your code goes here
            if (search == null || search.Length == 0)
            {
                return "";
            }

            // assume that arr is not empty
            if (source == null || source.Length == 0)
            {
                return "";
            }

            // put arr to the dictionary
            var map = new Dictionary<char, int>();
            foreach (var item in search)
            {
                map.Add(item, 1);
            }

            var needChars = search.Length; // 'xyz' - 3, var match = needChars == 0 
            // iterate the string and find match, and also keep track of minimum 
            var left = 0;
            var length = source.Length;

            var smallestLength = length + 1;  //  
            var smallestSubstring = "";

            for (int index = 0; index < length; index++)   //  
            {
                var visit = source[index];

                // x - 1 
                var inMap = map.ContainsKey(visit);
                var needOne = inMap && map[visit] > 0; // add checking contains
                if (inMap)
                {
                    map[visit]--;
                }

                if (needOne)
                {
                    needChars--; // take x off 
                }

                var findMatch = needChars == 0;
                if (!findMatch)
                {
                    continue;
                }

                // move left point forward - while loop                
                while (left <= index && (!map.ContainsKey(source[left]) || (map.ContainsKey(source[left]) && map[source[left]] < 0)))
                {
                    var removeChar = source[left];

                    // update the variable needChars xx -1                     
                    if (map.ContainsKey(source[left]))
                    {
                        map[removeChar]++;
                    }

                    left++;
                }

                var currentLength = index - left + 1;
                var findSmallerOne = currentLength < smallestLength;
                if (findSmallerOne)
                {
                    smallestLength = currentLength;
                    smallestSubstring = source.Substring(left, currentLength);

                    needChars++;
                    map[source[left]]++;
                    left = left + 1;
                }
            }

            // edge case
            if (smallestLength == length + 1)
            {
                return "";
            }
            else
            {
                return smallestSubstring;
            }
        }
    }
}
Jianmin Chen
  • 2.5k
  • 2
  • 28
  • 52