For this problem: https://leetcode.com/problems/search-suggestions-system/solution/, solution 1 states that for Java, _"there is an additional complexity of O(m^2) due to Strings being immutable, here m is the length of searchWord.") I am really scratching my head about this. Why the heck does immutability of string cause an additional runtime of O(m^2)?
Edit: I am trying to solve a Trie problem which involves creating a trie of words which are represented by character nodes ('c' -> 'a' -> 't'), and then searching for a word. When searching for a word, it is claimed that it takes an extra O(m^2) time in Java due to the immutability of strings in that language. Here is the solution provided by the website LeetCode:
// Custom class Trie with function to get 3 words starting with given prefix
class Trie {
// Node definition of a trie
class Node {
boolean isWord = false;
List<Node> children = Arrays.asList(new Node[26]);
};
Node Root, curr;
List<String> resultBuffer;
// Runs a DFS on trie starting with given prefix and adds all the words in the resultBuffer, limiting result size to 3
void dfsWithPrefix(Node curr, String word) {
if (resultBuffer.size() == 3)
return;
if (curr.isWord)
resultBuffer.add(word);
// Run DFS on all possible paths.
for (char c = 'a'; c <= 'z'; c++)
if (curr.children.get(c - 'a') != null)
dfsWithPrefix(curr.children.get(c - 'a'), word + c);
}
Trie() {
Root = new Node();
}
// Inserts the string in trie.
void insert(String s) {
// Points curr to the root of trie.
curr = Root;
for (char c : s.toCharArray()) {
if (curr.children.get(c - 'a') == null)
curr.children.set(c - 'a', new Node());
curr = curr.children.get(c - 'a');
}
// Mark this node as a completed word.
curr.isWord = true;
}
List<String> getWordsStartingWith(String prefix) {
curr = Root;
resultBuffer = new ArrayList<String>();
// Move curr to the end of prefix in its trie representation.
for (char c : prefix.toCharArray()) {
if (curr.children.get(c - 'a') == null)
return resultBuffer;
curr = curr.children.get(c - 'a');
}
dfsWithPrefix(curr, prefix);
return resultBuffer;
}
};
class Solution {
List<List<String>> suggestedProducts(String[] products,
String searchWord) {
Trie trie = new Trie();
List<List<String>> result = new ArrayList<>();
// Add all words to trie.
for (String w : products)
trie.insert(w);
String prefix = new String();
for (char c : searchWord.toCharArray()) {
prefix += c;
result.add(trie.getWordsStartingWith(prefix));
}
return result;
}
};
But to me it seems like instead of incrementing the string "prefix" one char at a time, and then searching through the trie for "prefix", I can instead iterate through the nodes of the trie one node at a time, and get the search suggestions from each node. This will completely remove the need to increment a "prefix" string, and from what I can tell will run in O(m) time for the search function (I'm not confused about the time required to build the trie). My solution is below. (Why) am I wrong?
class Solution {
TrieNode root;
public List<List<String>> suggestedProducts(String[] products, String searchWord) {
root = new TrieNode();
for( String each: products )
{
insert( each, root, 0 );
}
List<List<String>> sol = search( root, searchWord );
return sol;
}
public void insert( String str, TrieNode curr, int pos )
{
if( pos >= str.length() ) return;
if( !curr.map.containsKey( str.charAt(pos) ) )
{
TrieNode next = new TrieNode();
curr.map.put( str.charAt(pos), next );
curr = next;
}
else
{
curr = curr.map.get( str.charAt(pos) );
}
curr.strings.add( str );
insert( str, curr, pos + 1 );
}
public List<List<String>> search( TrieNode curr, String s )
{
List<List<String>> sol = new ArrayList<>();
boolean notFound = false;
for( int i = 0; i < s.length(); i++ )
{
List<String> row = new ArrayList<>();
if( notFound || !curr.map.containsKey( s.charAt(i) ) )
{
notFound = true;
sol.add( row );
continue;
}
else
{
curr = curr.map.get( s.charAt(i) );
List<String> strs = curr.strings;
Collections.sort( strs );
for( int c = 0; c < Math.min( 3, strs.size() ); c++ )
{
row.add( strs.get(c) );
}
sol.add( row );
}
}
return sol;
}
}
class TrieNode {
List<String> strings;
Map<Character, TrieNode> map;
public TrieNode() {
strings = new ArrayList<>();
map = new HashMap<>();
}
}
As comments mention please read How to Ask, if your question doesn't include code with out us scrolling through external resources it will likely be closed. The link is okay to have, just include code snippits, or sources that you have used.
To answer your question, every time you concatenate a string it is done in O(N) time as a new String has to be created with every concatenation, source.
So in the solution:
Since you concatenate N times at O(N) time each, you add O(N^2) time complexity, as opposed to a language like C++ where string concatenation time complexity is based on an array growth (which could be linear if a growth has to happen every time, but will in most cases be constant time).