我正在尝试用sml编写一个程序,该程序占用列表的长度,将出现在列表中的最大数量,当然还有列表。然后,它计算包含所有数字的最小“子列表”的长度。
我尝试使用滑动窗口方法,具有两个索引,即前和尾。前端先扫描,当找到一个数字时,它将写入地图中已经看到该数字的次数。如果程序找到所有数字,则调用尾号。尾巴扫描列表,如果发现一个数字被查看的次数大于1,则将其删除。
到目前为止,我尝试过的代码如下:
structure Key=
struct
type ord_key=int
val compare=Int.compare
end
fun min x y = if x>y then y else x;
structure mymap = BinaryMapFn ( Key );
fun smallest_sub(n,t,listall,map)=
let
val k=0
val front=0
val tail=0
val minimum= n;
val list1=listall;
val list2=listall;
fun increase(list1,front,k,ourmap)=
let
val number= hd list1
val elem=mymap.find(ourmap,number)
val per=getOpt(elem,0)+1
fun decrease(list2,tail,k,ourmap,minimum)=
let
val number=hd list2
val elem=mymap.find(ourmap,number)
val per=getOpt(elem,0)-1
val per1=getOpt(elem,0)
in
if k>t then
if (per1=1) then decrease(tl list2,tail+1,k-1,mymap.insert(ourmap,number,per),min minimum (front-tail))
else decrease(tl list2,tail+1,k,mymap.insert(ourmap,number,per),min minimum (front-tail))
else increase (list1, front,k,ourmap)
end
in
if t>k then
if (elem<>NONE) then increase (tl list1,front+1,k,mymap.insert(ourmap,number,per))
else increase(tl list1,front+1,k+1,mymap.insert(ourmap,number,per))
else (if (n>front) then decrease(list2,tail,k,ourmap,minimum) else minimum)
end
in
increase(list1,front,k,map)
end
fun solve (n,t,acc)= smallest_sub(n,t,acc,mymap.empty)
但是当我用这个smallest_sub(10,3,[1,3,1,3,1,3,3,2,2,2,1])称呼它时;这是行不通的。我做错了什么?
示例:如果输入为1,3,1,3,1,3,3,2,2,1,则程序应识别出包含所有数字且最小的列表中的parto为1,3, 3,2和3,2,2,1,因此输出应为4
答案 0 :(得分:2)
“包含所有值的最小子列表”的问题似乎再次出现 没有成功答案的新问题。这是因为它不是minimal, complete, and verifiable example。
因为您使用“滑动窗口”方法,所以索引了前和 在您输入的内容中,花费 O(n)时间来索引元素的列表并不理想。您 确实想在这里使用数组。如果您的输入函数必须有一个列表,则您 可以出于算法目的将其转换为数组。
我想在回答之前对代码进行清理,因为运行 您当前的手工代码有点难,因为它是如此的简洁。这是一个 如何提取给定是否给定的簿记的示例 子列表包含原始列表中每个元素的至少一个副本:
编辑::最初发布后,我更改了以下代码。
structure CountMap = struct
structure IntMap = BinaryMapFn(struct
type ord_key = int
val compare = Int.compare
end)
fun count (m, x) =
Option.getOpt (IntMap.find (m, x), 0)
fun increment (m, x) =
IntMap.insert (m, x, count (m, x) + 1)
fun decrement (m, x) =
let val c' = count (m, x)
in if c' <= 1
then NONE
else SOME (IntMap.insert (m, x, c' - 1))
end
fun flip f (x, y) = f (y, x)
val fromList = List.foldl (flip increment) IntMap.empty
end
也就是说,CountMap
是int IntMap.map
,其中Int
代表
地图的固定键类型,int
,其前面的参数int
表示地图的值类型,是此次数的 count
价值发生了。
在构建下面的initialCountMap
时,请使用CountMap.increment
,然后
当您使用“滑动窗口”方法时,可以使用CountMap.decrement
产生一个新的countMap
,您可以对其进行递归测试。
如果将出现的次数减少到1以下,那么您正在查看的子列表
至少每个元素都不包含一次;我们排除任何解决方案
让CountMap.decrement
返回NONE
。
将所有这些机制抽象出来,算法本身就变得非常重要 更容易表达。首先,我想将列表转换为数组,以便 索引变为 O(1),因为我们将做很多索引工作。
fun smallest_sublist_length [] = 0
| smallest_sublist_length (xs : int list) =
let val arr = Array.fromList xs
val initialCountMap = CountMap.fromList xs
fun go countMap i j =
let val xi = Array.sub (arr, i)
val xj = Array.sub (arr, j)
val decrementLeft = CountMap.decrement (countMap, xi)
val decrementRight = CountMap.decrement (countMap, xj)
in
case (decrementLeft, decrementRight) of
(SOME leftCountMap, SOME rightCountMap) =>
Int.min (
go leftCountMap (i+1) j,
go rightCountMap i (j-1)
)
| (SOME leftCountMap, NONE) => go leftCountMap (i+1) j
| (NONE, SOME rightCountMap) => go rightCountMap i (j-1)
| (NONE, NONE) => j - i + 1
end
in
go initialCountMap 0 (Array.length arr - 1)
end
这似乎可行,但是...
执行Int.min (go left..., go right...)
会产生 O(n ^ 2)堆栈的费用
记忆体(如果您不能排除其中之一是最佳的)。这是一个
动态编程的好用例,因为您的递归子问题有一个
常见子结构,即
go initialCountMap 0 10
|- go leftCountMap 1 10
| |- ...
| `- go rightCountMap 1 9 <-.
`- go rightCountMap 0 9 | possibly same sub-problem!
|- go leftCountMap 1 9 <-'
`- ...
所以也许有一种方法可以将递归子问题存储在内存阵列中,而不是 如果您知道此子问题的结果,请执行递归查找。如何 就SML而言,做备忘录本身就是一个很好的问题。纯粹怎么做 使用非惰性语言进行功能记忆是更好的选择。
您可以进行的另一种优化是,如果找到子列表,
大小独特元素的数量,您无需再寻找。这个号码
偶然是initialCountMap
和IntMap
中元素的数量
可能有找到它的功能。