diff --git a/LeetCodeSharp.Fetcher/Fetcher.cs b/LeetCodeSharp.Fetcher/Fetcher.cs new file mode 100644 index 0000000..ed1e2dd --- /dev/null +++ b/LeetCodeSharp.Fetcher/Fetcher.cs @@ -0,0 +1,81 @@ +using System.Net.Http.Json; +using System.Text.Json; +using LeetCodeSharp.Fetcher.Models; + +namespace LeetCodeSharp.Fetcher; + +internal class Fetcher +{ + private const string ProblemsUrl = "https://leetcode.cn/api/problems/algorithms/"; + private const string GraphQlUrl = "https://leetcode.cn/graphql"; + + private readonly HttpClient _httpClient = new(); + + private readonly JsonSerializerOptions _serializerOptions = new() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + public async Task GetProblems() + { + var problems = + await _httpClient.GetFromJsonAsync(ProblemsUrl, _serializerOptions) + ?? throw new Exception("Failed to get problems."); + + return problems; + } + + public async Task GetProblem(uint questionId) + { + var problems = await GetProblems(); + + var targetProblem = problems.StatStatusPairs.First(p => + { + if (uint.TryParse(p.Stat.FrontendQuestionId, out var id)) + { + return id == questionId; + } + + return false; + }); + + if (targetProblem.PaidOnly) + { + throw new Exception("Target problem is paid only."); + } + + if (targetProblem.Stat.QuestionTitleSlug is null) + { + throw new Exception("Failed to get problem title."); + } + + var query = new Query(targetProblem.Stat.QuestionTitleSlug); + var response = await _httpClient.PostAsJsonAsync( + GraphQlUrl, query, _serializerOptions); + response.EnsureSuccessStatusCode(); + + var rawProblem = await JsonSerializer.DeserializeAsync( + await response.Content.ReadAsStreamAsync(), _serializerOptions) + ?? throw new Exception("Failed to get raw problem."); + + var returnType = rawProblem.Data.Question.MetaData.Replace("\"", ""); + + return new Problem + { + Title = targetProblem.Stat.QuestionTitle + ?? throw new Exception("Failed to get question title"), + TitleSlug = targetProblem.Stat.QuestionTitleSlug + ?? throw new Exception("Failed to get question title slug"), + Content = rawProblem.Data.Question.Content, + QuestionId = questionId, + ReturnType = returnType, + CodeDefinition = JsonSerializer.Deserialize>( + rawProblem.Data.Question.CodeDefinition, _serializerOptions) + ?? throw new Exception("Failed to get code definition.") + }; + } + + +} + diff --git a/LeetCodeSharp.Fetcher/LeetCodeSharp.Fetcher.csproj b/LeetCodeSharp.Fetcher/LeetCodeSharp.Fetcher.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/LeetCodeSharp.Fetcher/LeetCodeSharp.Fetcher.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/LeetCodeSharp.Fetcher/Models/CodeDefinition.cs b/LeetCodeSharp.Fetcher/Models/CodeDefinition.cs new file mode 100644 index 0000000..94b8da8 --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/CodeDefinition.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace LeetCodeSharp.Fetcher.Models; + +internal class CodeDefinition +{ + public required string Value { get; set; } + + public required string Text { get; set; } + + [JsonPropertyName("defaultCode")] + public required string DefaultCode { get; set; } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Models/Problem.cs b/LeetCodeSharp.Fetcher/Models/Problem.cs new file mode 100644 index 0000000..4cf392b --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/Problem.cs @@ -0,0 +1,79 @@ +using System.Text; + +namespace LeetCodeSharp.Fetcher.Models; + +internal class Problem +{ + private const string Template = """ + /** + * [__PROBLEM_ID__] __PROBLEM_TITLE__ + */ + __EXTRA_USE__ + + namespace LeetCodeSharp.Problems + { + + // Submission codes start here + + __PROBLEM_CODE__ + + // Submission codes end here + } + """; + + public required string Title { get; set; } + + public required string TitleSlug { get; set; } + + public required string Content { get; set; } + + public required List CodeDefinition { get; set; } + + public required uint QuestionId { get; set; } + + public required string ReturnType { get; set; } + + public string GetFilename() + { + return $"p{QuestionId}_{TitleSlug.Replace('-', '_')}.cs"; + } + + public string GetFileContent() + { + var code = CodeDefinition.FirstOrDefault(c => c.Value == "csharp") + ?? throw new Exception("Target question has no C# version."); + + string template; + if (code.DefaultCode.Contains("public class ListNode") + || code.DefaultCode.Contains("public class Point") + || code.DefaultCode.Contains("public class TreeNode") + || code.DefaultCode.Contains("public class Node")) + { + template = Template.Replace("__EXTRA_USE__", "using LeetCodeSharp.Utils;"); + } + else + { + template = Template.Replace("__EXTRA_USE__", string.Empty); + } + + return template.Replace("__PROBLEM_ID__", Title) + .Replace("__PROBLEM_TITLE__", QuestionId.ToString()) + .Replace("__PROBLEM_CODE__", code.DefaultCode); + } + + public override string ToString() + { + var builder = new StringBuilder(); + + builder.Append("Title:").Append(Title).Append("\r\n"); + builder.Append("Title slug: ").Append(TitleSlug).Append("\r\n"); + builder.Append("Code definitions: \r\n"); + foreach (var definition in CodeDefinition) + { + builder.Append('\t').Append(definition.Value).Append("\r\n"); + } + builder.Append("Return type: ").Append(ReturnType).Append("\r\n"); + + return builder.ToString(); + } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Models/Problems.cs b/LeetCodeSharp.Fetcher/Models/Problems.cs new file mode 100644 index 0000000..2868f0b --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/Problems.cs @@ -0,0 +1,6 @@ +namespace LeetCodeSharp.Fetcher.Models; + +internal class Problems +{ + public required List StatStatusPairs { get; set; } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Models/Query.cs b/LeetCodeSharp.Fetcher/Models/Query.cs new file mode 100644 index 0000000..09b8737 --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/Query.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace LeetCodeSharp.Fetcher.Models; + +internal class Query +{ + [JsonPropertyName("operationName")] + public string OperationName { get; init; } + + public Dictionary Variables { get; } = []; + + [JsonPropertyName("query")] + public string QueryString { get; init; } + + public Query(string title) + { + OperationName = "questionData"; + Variables.Add("titleSlug", title); + QueryString = """ + query questionData($titleSlug: String!) { + question(titleSlug: $titleSlug) { + content + stats + codeDefinition + sampleTestCase + metaData + } + } + """; + } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Models/Question.cs b/LeetCodeSharp.Fetcher/Models/Question.cs new file mode 100644 index 0000000..d1ff28f --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/Question.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +namespace LeetCodeSharp.Fetcher.Models; + +internal class Question +{ + public required string Content { get; set; } + + public required string Stats { get; set; } + + [JsonPropertyName("codeDefinition")] + public required string CodeDefinition { get; set; } + + [JsonPropertyName("sampleTestCase")] + public required string SampleTestCase { get; set; } + + [JsonPropertyName("metaData")] + public required string MetaData { get; set; } +} + +internal class Data +{ + public required Question Question { get; set; } +} + +internal class RawProblem +{ + public required Data Data { get; set; } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Models/Stat.cs b/LeetCodeSharp.Fetcher/Models/Stat.cs new file mode 100644 index 0000000..c66fee4 --- /dev/null +++ b/LeetCodeSharp.Fetcher/Models/Stat.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace LeetCodeSharp.Fetcher.Models; + +internal class Stat +{ + public required uint QuestionId { get; set; } + + [JsonPropertyName("question__article_slug")] + public string? QuestionArticleSlug { get; set; } + + [JsonPropertyName("question__title")] + public string? QuestionTitle { get; set; } + + [JsonPropertyName("question__title_slug")] + public string? QuestionTitleSlug { get; set; } + + [JsonPropertyName("question__hide")] + public bool QuestionHide { get; set; } + + public required uint TotalAcs { get; set; } + + public required uint TotalSubmitted { get; set; } + + public required string FrontendQuestionId { get; set; } + + public required bool IsNewQuestion { get; set; } +} + +internal class StatWithStatus +{ + public required Stat Stat { get; set; } + + public required bool PaidOnly { get; set; } +} \ No newline at end of file diff --git a/LeetCodeSharp.Fetcher/Program.cs b/LeetCodeSharp.Fetcher/Program.cs new file mode 100644 index 0000000..7997fb8 --- /dev/null +++ b/LeetCodeSharp.Fetcher/Program.cs @@ -0,0 +1,18 @@ +using LeetCodeSharp.Fetcher; + +var directories = Directory.EnumerateDirectories(Environment.CurrentDirectory); + +if (!directories.Any(d => d.Contains("Problems"))) +{ + Console.WriteLine("Please run in correct directory!"); + return; +} + +var problemDirectory = Path.Combine(Environment.CurrentDirectory, "Problems"); +var fetcher = new Fetcher(); + +var problem = await fetcher.GetProblem(uint.Parse(args[0])); +var problemFilename = Path.Combine(problemDirectory, problem.GetFilename()); + +await using var writer = new StreamWriter(problemFilename); +await writer.WriteAsync(problem.GetFileContent()); \ No newline at end of file diff --git a/LeetCodeSharp.sln b/LeetCodeSharp.sln new file mode 100644 index 0000000..b503798 --- /dev/null +++ b/LeetCodeSharp.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34607.119 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LeetCodeSharp", "LeetCodeSharp\LeetCodeSharp.csproj", "{EB1DDDC1-A2FC-4AAB-A21A-59FAAFF656DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LeetCodeSharp.Fetcher", "LeetCodeSharp.Fetcher\LeetCodeSharp.Fetcher.csproj", "{5D45BB2A-E512-4353-8746-6F0B79282C76}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB1DDDC1-A2FC-4AAB-A21A-59FAAFF656DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB1DDDC1-A2FC-4AAB-A21A-59FAAFF656DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB1DDDC1-A2FC-4AAB-A21A-59FAAFF656DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB1DDDC1-A2FC-4AAB-A21A-59FAAFF656DD}.Release|Any CPU.Build.0 = Release|Any CPU + {5D45BB2A-E512-4353-8746-6F0B79282C76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D45BB2A-E512-4353-8746-6F0B79282C76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D45BB2A-E512-4353-8746-6F0B79282C76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D45BB2A-E512-4353-8746-6F0B79282C76}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D28602CA-DA79-4D3C-A539-2FEB01BBEDA4} + EndGlobalSection +EndGlobal diff --git a/LeetCodeSharp.sln.DotSettings b/LeetCodeSharp.sln.DotSettings new file mode 100644 index 0000000..402a927 --- /dev/null +++ b/LeetCodeSharp.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/LeetCodeSharp/LeetCodeSharp.csproj b/LeetCodeSharp/LeetCodeSharp.csproj new file mode 100644 index 0000000..e3bf522 --- /dev/null +++ b/LeetCodeSharp/LeetCodeSharp.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.1 + disable + disable + + + + + + + diff --git a/LeetCodeSharp/Problems/p589_n_ary_tree_preorder_traversal.cs b/LeetCodeSharp/Problems/p589_n_ary_tree_preorder_traversal.cs new file mode 100644 index 0000000..c1fe2f1 --- /dev/null +++ b/LeetCodeSharp/Problems/p589_n_ary_tree_preorder_traversal.cs @@ -0,0 +1,63 @@ +/** +* [N-ary Tree Preorder Traversal] 589 +*/ + + +using LeetCodeSharp.Utils; +using System.Collections.Generic; + +namespace LeetCodeSharp.Problems +{ + + // Submission codes start here + + /* +// Definition for a Node. +public class Node { + public int val; + public IList children; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val,IList _children) { + val = _val; + children = _children; + } +} +*/ + + public partial class Solution + { + public IList Preorder(Node root) + { + + var dfs = new Dfs(); + + dfs.Search(root); + return dfs.Result; + } + + private class Dfs + { + public IList Result { get; } = new List(); + + public void Search(Node node) + { + if (node == null) return; + + Result.Add(node.val); + + foreach (var child in node.children) + { + Search(child); + } + } + } + } + + // Submission codes end here +} \ No newline at end of file diff --git a/LeetCodeSharp/Utils/ListNode.cs b/LeetCodeSharp/Utils/ListNode.cs new file mode 100644 index 0000000..12a7ea0 --- /dev/null +++ b/LeetCodeSharp/Utils/ListNode.cs @@ -0,0 +1,15 @@ +namespace LeetCodeSharp.Utils +{ + public class ListNode + { + public int val; + + public ListNode next; + + public ListNode(int val = 0, ListNode next = null) + { + this.val = val; + this.next = next; + } + } +} diff --git a/LeetCodeSharp/Utils/Node.cs b/LeetCodeSharp/Utils/Node.cs new file mode 100644 index 0000000..443df9f --- /dev/null +++ b/LeetCodeSharp/Utils/Node.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace LeetCodeSharp.Utils +{ + public class Node + { + public int val; + public IList children; + + public Node() { } + + public Node(int _val) + { + val = _val; + } + + public Node(int _val, IList _children) + { + val = _val; + children = _children; + } + } +} diff --git a/LeetCodeSharp/Utils/Point.cs b/LeetCodeSharp/Utils/Point.cs new file mode 100644 index 0000000..0e9a873 --- /dev/null +++ b/LeetCodeSharp/Utils/Point.cs @@ -0,0 +1,15 @@ +namespace LeetCodeSharp.Utils +{ + public class Point + { + public int x; + + public int y; + + public Point(int x, int y) + { + this.x = x; + this.y = y; + } + } +} diff --git a/LeetCodeSharp/Utils/TreeNode.cs b/LeetCodeSharp/Utils/TreeNode.cs new file mode 100644 index 0000000..2347be6 --- /dev/null +++ b/LeetCodeSharp/Utils/TreeNode.cs @@ -0,0 +1,18 @@ +namespace LeetCodeSharp.Utils +{ + public class TreeNode + { + public int val; + + public TreeNode left; + + public TreeNode right; + + public TreeNode(int val = 0, TreeNode left = null, TreeNode right = null) + { + this.val = val; + this.left = left; + this.right = right; + } + } +}