有没有更好的方法(最快)来获得最长的公共文件夹路径?

Posted

技术标签:

【中文标题】有没有更好的方法(最快)来获得最长的公共文件夹路径?【英文标题】:Is there a better way (fastest) to get the longest common folder path? 【发布时间】:2021-07-16 10:34:43 【问题描述】:

我有一个文件夹列表, 想要得到最长的公共子串作为输出。

这是我的代码,好像不太好,

如何改进这些?

这些文件都来自我的RecursiveSearch函数,可能是D:\123, E:\456, F:\a\e\eff,我保存到xml。

稍后从 xml 中读回,想要得到之前的决定。

文件可能有一万个。 我确实关心速度。

var li = new List<string>()

    @"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
    @"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
    @"C:\Users\jared\Desktop\fld4\ade\4.jfif",
    @"C:\Users\jared\Desktop\fld4\abc\S.png",
    @"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
;

//var shortest1 = li.OrderBy(name => name.Length).FirstOrDefault();
string shortest = li.Aggregate((a1, a2) => a1.Length < a2.Length ? a1 : a2);
string longest = li.Aggregate((a1, a2) => a1.Length > a2.Length ? a1 : a2);
string common = string.Concat(shortest.TakeWhile((c, i) => c == longest[i]));
// C:\Users\jared\Desktop\fld       NOT MY WANT CAUSE THERE IS NO SUCH FOLDER.

if (!Directory.Exists(common))

    common = common.Replace(common.Split("\\").Last(), "");

【问题讨论】:

你保证有共同的路径吗? (即所有文件总是在 C 盘上吗?) @Damien_The_Unbeliever 是的。这些文件都来自 My RecursiveSearch 函数,可能是 D:\123, E:\456, F:\a\e\eff,我保存到 xml。我从 xml 回读,想要得到我之前的决定。 为什么需要更快的方法?您是否衡量过这是性能瓶颈? 你写:// C:\Users\jared\Desktop\fld NOT MY WANT. 那么你的预期结果是什么? OP 可能想找到C:\Users\jared\Desktop 【参考方案1】:

您的方法将失败,因为您只是比较 shortestlongest,如果有任何其他长度不同且中间有不同路径的路径。

根据我的理解,您希望输出到匹配的文件夹(不是文件夹或文件名的子字符串),即C:\Users\jared\Desktop\

这是我在 O(Nk) 解决方案中运行的解决方案,其中 N 是路径数,k 是最短可用长度。

工作示例代码here。

这也可以使用 Trie 数据结构以更优化的方式 (O(N + k)) 完成,但构建 Trie 需要额外的空间

    var folders = new List<string>()
    
        @"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
        @"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
        @"C:\Users\jared\Desktop\fld4\ade\4.jfif",
        @"C:\Users\jared\Desktop\fld4\abc\S.png",
        @"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
    ;
    
    var minPathLength = folders.Min(x => x.Length);
    
    var maxCommonPath = new StringBuilder();
    var currentCommonPath = new StringBuilder();
    for (int i = 0; i < minPathLength; i++)
    
        var boolAllSame = true;
        var c = folders[0][i];
        boolAllSame = folders.All(x => x[i] == c);
        
        if (boolAllSame)
        
            currentCommonPath.Append(c);
            if (c == '\\')
            
                maxCommonPath.Append(currentCommonPath.ToString());
                currentCommonPath = new StringBuilder();
            
        
        else
            break;
    
    
    var result = maxCommonPath.ToString();
    Console.WriteLine(result);

【讨论】:

如果输入路径仍然需要更多时间,请尝试使用运行速度更快的 Trie 数据结构。 您可以考虑比较不区分大小写的字符 (Char.ToUpperInvariant),以允许匹配具有大小写差异的相等路径。 @TheodorZoulias,你是对的,但由于我们不确定输入,让 Jared 决定他想要什么。感谢您提出这个问题。【参考方案2】:

这是一个使用 Linq 的简洁但不是特别高效的实现。它还依赖于MoreLinq 包,对于Transpose 运算符:

var paths = new List<string>()

    @"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
    @"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
    @"C:\Users\jared\Desktop\fld4\ade\4.jfif",
    @"C:\Users\jared\Desktop\fld4\abc\S.png",
    @"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
;
string[] longestCommonPathComponents = paths
    .Select(path => path.Split(Path.DirectorySeparatorChar))
    .Transpose()
    .Select(parts => parts.Distinct(StringComparer.OrdinalIgnoreCase))
    .TakeWhile(distinct => distinct.Count() == 1)
    .Select(distinct => distinct.First())
    .ToArray();
string longestCommonPath = Path.Combine(longestCommonPathComponents);
Console.WriteLine($"Longest common path: longestCommonPath");

输出:

最长公共路径:C:/Users/jared/Desktop

Try it on fiddle.

Transpose 操作符的签名:

// Transposes a sequence of rows into a sequence of columns.
public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source);

【讨论】:

【参考方案3】:
var li = new List<string>()

    @"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
    @"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
    @"C:\Users\jared\Desktop\fld4\ade\4.jfif",
    @"C:\Users\jared\Desktop\fld4\abc\S.png",
    @"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
;

string first = li.First();
int n = 0;

while (li.All(x => x.Length > n && x[n] == first[n]))
    n++;

string longestCommon = first.Substring(0, n); // C:\Users\jared\Desktop\fld

你说C:\Users\jared\Desktop\fld不是你想要的,而是你想要的?最嵌套的有效文件夹?只需拉到最后一个斜线,或使用 Path 方法来获取您想要的位。

【讨论】:

没有这样的文件夹:C:\Users\jared\Desktop\fld 所以按照我的建议做,然后拉到最后一个斜线?例如var folder = longestCommon.Substring(0, longestCommon.LastIndexOf('\'));【参考方案4】:

有一个很好且快速的方法。好吧,你犯了一个大错误。 您不应该将文件保存在桌面上。它们必须位于项目路径和 bin\debug 文件夹中。也许你认为文件路径变长了,但是有一行代码是短而优化的。

var li = new List<string>()

Application.StartupPath + "/Images/1.png",
Application.StartupPath + "/Images/2.png",
Application.StartupPath + "/Images/3.png",

【讨论】:

你真的读过这个问题吗?这根本没有任何相关性。 应用程序将xml保存在应用程序文件夹中,xml文件记录Desktop文件夹中的文件。

以上是关于有没有更好的方法(最快)来获得最长的公共文件夹路径?的主要内容,如果未能解决你的问题,请参考以下文章

有没有更好的方法来要求来自 lua 中相对路径的文件

最长公共子序列长度函数没有返回正确的长度?

在 O(NlogN) 时间内找到最长的公共子序列

题14:最长公共前缀

使用 DELETE 逻辑不在嵌套查询中的最快性能 [重复]

有没有更好的方法来调整gerrit插入代码大小基于规则和排除文件路径?