我有一个问题,要求通过将字符串的初始值的副本附加到自身上来将字符串转换为另一个字符串。该问题允许在某些位置删除单个字符。
说明
let x = "abba"; // First string
let y = "aba" // Second initial string
y("aba") => remove last "a" => y("ab") => y+initialY = "ab"+"aba" =>
y("ababa") => remove char at index 2 => y("abba") => y == x => sucess
我的算法成功解决了这个问题:
let x = "abbbbcccaaac"
let y = "abc"
let xArr = x.split('')
let yArr = y.split('')
let count = 0;
for (let i = 0; i < xArr.length; i++) {
if(yArr[i] == undefined) {
yArr = yArr.concat(y.split('')
count++;
}
if(xArr[i] != yArr[i]) {
yArr.splice(i, 1);
i--;
}
}
console.log("Output is:", yArr.join(''))
console.log("Appends in order to transform:", count)
该算法按预期工作,但是,我不确定它的时间和空间复杂度,最重要的是效率。
此算法是否在O(n)
时间复杂度中,其中n是x
的长度?
如果不是O(n)
,可以在O(n)
时间内解决问题吗?
.concat()
,.splice()
或.split()
是否因为嵌套在for循环中而以某种方式改变了时间复杂度?如果不是的话,它们还会改变算法的时间复杂度以及改变多少吗?
给出此问题的规则,这是解决此问题的有效方法吗?
此算法的空间复杂度是什么?
答案 0 :(得分:1)
通常,这样的问题很难给出一个明确的答案,因为Javascript的不同实现对基本数组操作(例如creating a new array of size n)具有不同的时间复杂度。 Javascript数组通常将以dynamic arrays或hashtables的形式实现,并且这些数据结构具有不同的性能特征。
因此,splice
从数组中删除一个元素没有确定的时间复杂度。我们可以说的是,删除一个元素需要一个动态数组线性时间,正如@ Ry-在注释中指出的那样,哈希表也需要线性时间,因为需要重新编号后面的索引。我们也可以说,很可能使用了这两种数据结构中的一种,并且没有明智的实现比线性时间花更多的时间来完成splice
。
无论哪种方式,对于您的算法而言,最坏的情况是当x = 'aa...aa'
和y = 'abb...bb'
,即x
是'a'
的n个副本,而y
是{ {1}}后跟(m-1)份'a'
的副本。
对于动态数组或哈希表,则仅'b'
操作的时间复杂度为O(nm²)。这是因为外部循环迭代O(nm)次(请注意循环内部的splice
,每次需要删除字母i--
时都会发生),并且'b'
操作需要在索引splice
之后对yArr
中的O(m)个元素进行移位或重新编号。
但是假设使用了一些更奇特的数据结构,该结构支持在亚线性时间内删除元素(例如skip list)。在这种情况下,上面的代码仅给出O(nm)乘以“删除”操作的复杂度。但是我们还没有计算i
;这将创建一个新的数据结构并将每个项目复制到其中,这仍然需要线性时间。 concat
被称为O(n)次,每次调用平均需要O(n + m)次,因此,concat
运算的复杂度为O(n²+ nm)。
因此时间复杂度很可能是O(n²+nm²),当然至少是O(n²+ nm);不是线性的。
空间复杂度为O(n),因为concat
的长度永远不会超过yArr
的两倍。