নূন্যতম শব্দ অনুসন্ধান


18

গত সপ্তাহে, আমরা ইংরেজি ভাষার শীর্ষে 10,000 শব্দ ব্যবহার করে সংক্ষিপ্ততম 1-ডি স্ট্রিং তৈরি করতে কাজ করেছি । এখন, 2D তে একই চ্যালেঞ্জটি চেষ্টা করে দেখি!

আপনাকে যা করতে হবে তা হল উপরের সমস্ত শব্দের গ্রহণ করা এবং সেগুলি ওভারল্যাপের জন্য অনুমতি দিয়ে যথাসম্ভব ছোট একটি আয়তক্ষেত্রের মধ্যে রেখে দেওয়া। উদাহরণস্বরূপ, যদি আপনার শব্দগুলি হয় ["ape","pen","ab","be","pa"], তবে একটি সম্ভাব্য আয়তক্ষেত্রটি হতে পারে:

.b..
apen

উপরের আয়তক্ষেত্রটি স্কোর দেবে।

নিয়মাবলী:

  • একটি শব্দে একাধিক অক্ষর ওভারল্যাপিং অনুমোদিত
  • শব্দ 8 দিকের যে কোনও একটিতে যেতে পারে
  • শব্দ চারপাশে মোড়ানো করতে পারে না
  • আপনি খালি জায়গাগুলির জন্য যে কোনও অক্ষর ব্যবহার করতে পারেন

আপনাকে এমন একটি শব্দ অনুসন্ধান তৈরি করতে হবে যা ইংরেজিতে (গুগল অনুসারে) শীর্ষস্থানীয় 10,000 শব্দ ধারণ করে । আপনার স্কোরটি আপনার শব্দ অনুসন্ধানে অক্ষরের সংখ্যার সমান (অব্যবহৃত অক্ষরগুলি বাদ দিয়ে)। যদি কোনও টাই থাকে, বা কোনও জমাটি সর্বোত্তম প্রমাণিত হয়, তবে প্রথমে পোস্ট করা জমাটি জয়লাভ করে।


1
আমি লক্ষ করতে চাই যে আমি এই পূর্ববর্তী শব্দ অনুসন্ধান চ্যালেঞ্জ সম্পর্কে সচেতন , কিন্তু সেখানে দেওয়া উত্তরগুলির মধ্যে কোনওটিই এই চ্যালেঞ্জের জন্য যুক্তিসঙ্গত সময়ে চালিত হবে না, আমি এর সদৃশ বিশ্বাস করি না।
নাথান মেরিল


আমি আশঙ্কা করি যে সর্বোত্তম সমাধানটি এনএক্স 1 গ্রিড হিসাবে পরিণত হবে, এই সমস্যাটি শেষ পর্যন্ত একই হিসাবে তৈরি করবে (যুক্তি: স্পর্শকাতর ছেদগুলি খুব কম অক্ষর সংরক্ষণ করবে তবে প্রায়শই "গর্ত" প্রবর্তন করবে, স্থান নষ্ট করবে)। হতে পারে আপনার প্রস্থের * উচ্চতার চেয়ে প্রস্থ + উচ্চতায় স্কোর করা উচিত, যাতে এটি দৃ square়ভাবে বর্গক্ষেত্র সমাধানের পক্ষে (আরও আকর্ষণীয়) ors
ডেভ

হুম ... আমি আশঙ্কা করি যে সমাধানগুলি কেবল তখন একে অপরের উপরে স্তরে স্তরে স্তরের স্ট্রাকড হয়ে থাকবে word আমি মনে করি খালি জায়গাগুলি স্কোর না করা ভাল ধারণা হতে পারে
নাথান মেরিল

এর সাথে ঝুঁকি গ্রিডের আকার ছোট রাখার দরকার নেই; একটি স্প্রোলিং অনুভূমিক এবং উল্লম্ব তালিকা সহ একটি 1000x1000 গ্রিড একটি আঁটসাঁট সর্পিল প্যাটার্ন / অনুরূপ হিসাবে একই স্কোর করবে। প্রস্থ + উচ্চতা চেষ্টা করুন, তারপরে টাই-ব্রেকার হিসাবে অক্ষর-বাদ দিয়ে ফাঁকা? আরও কিছুটা চিন্তাভাবনা করা দরকার। সম্পাদনা: অথবা সম্ভবত অক্ষর-বাদ দিয়ে ফাঁকা প্রথমে টাই-ব্রেকার হিসাবে প্রস্থ + উচ্চতা আরও ভাল কাজ করবে।
ডেভ

উত্তর:


7

মরিচা, 31430 30081 টি ব্যবহৃত অক্ষর

এটি প্রকারের একটি লোভী অ্যালগরিদম: আমরা একটি খালি গ্রিড দিয়ে শুরু করি, এবং বার বার এমন শব্দ যুক্ত করি যা সংক্ষেপে আরও কয়েকটি নতুন অক্ষর যুক্ত করা যেতে পারে, লম্বা শব্দের সাথে তাল মিলিয়ে ভেঙে দেওয়া। এই রানটি দ্রুত চালানোর জন্য, আমরা প্রার্থী শব্দের স্থান নির্ধারণের একটি অগ্রাধিকার সারি বজায় রাখি (প্রতিটি শব্দের দৈর্ঘ্যের জন্য একটি ডেকযুক্ত প্রত্যেকটি নতুন অক্ষরের ভেক্টর সহ ডেকের ভেক্টরগুলির ভেক্টর হিসাবে প্রয়োগ করা হয়)। প্রতিটি নতুন যুক্ত হওয়া চিঠির জন্য, আমরা সেই চিঠির মধ্য দিয়ে চলমান সমস্ত প্রার্থীর স্থান নির্ধারণ করি।

সংকলন এবং সাথে চালানো rustc -O wordsearch.rs; ./wordsearch < google-10000-english.txt। আমার ল্যাপটপে, এটি 531 মাইবি র‌্যাম ব্যবহার করে 70 সেকেন্ডে চলে।

আউটপুট 248 কলাম এবং 253 সারি সহ একটি আয়তক্ষেত্রে ফিট করে।

এখানে চিত্র বর্ণনা লিখুন

কোড

use std::collections::{HashMap, HashSet, VecDeque};
use std::io::prelude::*;
use std::iter::once;
use std::vec::Vec;

type Coord = i16;
type Pos = (Coord, Coord);
type Dir = u8;
type Word = u16;

struct Placement { word: Word, dir: Dir, pos: Pos }

static DIRS: [Pos; 8] =
    [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)];

fn fit(grid: &HashMap<Pos, u8>, (x, y): Pos, d: Dir, word: &String) -> Option<usize> {
    let (dx, dy) = DIRS[d as usize];
    let mut n = 0;
    for (i, c) in word.bytes().enumerate() {
        if let Some(c1) = grid.get(&(x + (i as Coord)*dx, y + (i as Coord)*dy)) {
            if c != *c1 {
                return None;
            }
        } else {
            n += 1;
        }
    }
    return Some(n)
}

struct PlacementQueue { queue: Vec<Vec<VecDeque<Placement>>>, extra: usize }

impl PlacementQueue {
    fn new() -> PlacementQueue {
        return PlacementQueue { queue: Vec::new(), extra: std::usize::MAX }
    }

    fn enqueue(self: &mut PlacementQueue, extra: usize, total: usize, placement: Placement) {
        while self.queue.len() <= extra {
            self.queue.push(Vec::new());
        }
        while self.queue[extra].len() <= total {
            self.queue[extra].push(VecDeque::new());
        }
        self.queue[extra][total].push_back(placement);
        if self.extra > extra {
            self.extra = extra;
        }
    }

    fn dequeue(self: &mut PlacementQueue) -> Option<Placement> {
        while self.extra < self.queue.len() {
            let mut subqueue = &mut self.queue[self.extra];
            while !subqueue.is_empty() {
                let total = subqueue.len() - 1;
                if let Some(placement) = subqueue[total].pop_front() {
                    return Some(placement);
                }
                subqueue.pop();
            }
            self.extra += 1;
        }
        return None
    }
}

fn main() {
    let stdin = std::io::stdin();
    let all_words: Vec<String> =
        stdin.lock().lines().map(|l| l.unwrap()).collect();
    let words: Vec<&String> = {
        let subwords: HashSet<&str> =
            all_words.iter().flat_map(|word| {
                (0..word.len() - 1).flat_map(move |i| {
                    (i + 1..word.len() - (i == 0) as usize).map(move |j| {
                        &word[i..j]
                    })
                })
            }).collect();
        all_words.iter().filter(|word| !subwords.contains(&word[..])).collect()
    };
    let letters: Vec<Vec<(usize, usize)>> =
        (0..128).map(|c| {
            words.iter().enumerate().flat_map(|(w, word)| {
                word.bytes().enumerate().filter(|&(_, c1)| c == c1).map(move |(i, _)| (w, i))
            }).collect()
        }).collect();

    let mut used = vec![false; words.len()];
    let mut remaining = words.len();
    let mut grids: Vec<HashMap<Pos, u8>> = Vec::new();

    while remaining != 0 {
        let mut grid: HashMap<Pos, u8> = HashMap::new();
        let mut queue = PlacementQueue::new();
        for (w, word) in words.iter().enumerate() {
            if used[w] {
                continue;
            }
            queue.enqueue(0, word.len(), Placement {
                pos: (0, 0),
                dir: 0,
                word: w as Word
            });
        }

        while let Some(placement) = queue.dequeue() {
            if used[placement.word as usize] {
                continue;
            }
            let word = words[placement.word as usize];
            if let None = fit(&grid, placement.pos, placement.dir, word) {
                continue;
            }
            let (x, y) = placement.pos;
            let (dx, dy) = DIRS[placement.dir as usize];
            let new_letters: Vec<(usize, u8)> = word.bytes().enumerate().filter(|&(i, _)| {
                !grid.contains_key(&(x + (i as Coord)*dx, y + (i as Coord)*dy))
            }).collect();
            for (i, c) in word.bytes().enumerate() {
                grid.insert((x + (i as Coord)*dx, y + (i as Coord)*dy), c);
            }
            used[placement.word as usize] = true;
            remaining -= 1;

            for (i, c) in new_letters {
                for &(w1, j) in &letters[c as usize] {
                    if used[w1] {
                        continue;
                    }
                    let word1 = words[w1];
                    for (d1, &(dx1, dy1)) in DIRS.iter().enumerate() {
                        let pos1 = (
                            x + (i as Coord)*dx - (j as Coord)*dx1,
                            y + (i as Coord) - (j as Coord)*dy1);
                        if let Some(extra1) = fit(&grid, pos1, d1 as Dir, word1) {
                            queue.enqueue(extra1, word1.len(), Placement {
                                pos: pos1,
                                dir: d1 as Dir,
                                word: w1 as Word
                            });
                        }
                    }
                }
            }
        }
        grids.push(grid);
    }

    let width = grids.iter().map(|grid| {
        grid.iter().map(|(&(x, _), _)| x).max().unwrap() -
            grid.iter().map(|(&(x, _), _)| x).min().unwrap() + 1
    }).max().unwrap();
    print!(
        "{}",
        grids.iter().flat_map(|grid| {
            let x0 = grid.iter().map(|(&(x, _), _)| x).min().unwrap();
            let y0 = grid.iter().map(|(&(_, y), _)| y).min().unwrap();
            let y1 = grid.iter().map(|(&(_, y), _)| y).max().unwrap();
            (y0..y1 + 1).flat_map(move |y| {
                (x0..x0 + width).map(move |x| {
                    *grid.get(&(x, y)).unwrap_or(&('.' as u8)) as char
                }).chain(once('\n').take(1))
            })
        }).collect::<String>()
    );
}

আমি এখনও কোডটি পড়ি নি, তবে আপনি কি লিনিয়ার অ-অবস্থান নির্ধারণের জন্য কিছু করতে চান? আমি এমন একটি অ্যালগরিদম আশা করতাম সুপার মুদ্রাগুলি অতিক্রম করার জন্য কয়েক মুহুর্তের সাথে শেষ হয়ে যাবে তবে দেখে মনে হচ্ছে আপনি বেশ ভাল জায়গা পূরণ করছেন।
ডেভ

@ ডেভ কিছুই নির্দিষ্ট নয়, এটি ঠিক সেভাবে কার্যকর হয়। সুপার-স্ট্রিংগুলি কখনই এত দীর্ঘ হয় না যে আরও ভাল অ-রৈখিক প্লেসমেন্টগুলি আর কখনও খুঁজে পাওয়া যায় না, সম্ভবত আরও অনেকগুলি অ-রৈখিক প্লেসমেন্টগুলি বেছে নিতে পারে।
অ্যান্ডারস কাসের্গ

"অভিনন্দন" দিয়ে শুরু হয়, "অসাধারণ" দিয়ে শেষ হয়
আপনি

আমি বুঝতে পারিনি যে আপনিও তির্যক হয়ে যেতে পারেন। ছবির জন্য ধন্যবাদ আমি জানি না কোড কোডগুলিতে আমার মন্তব্য করা উচিত কিনা। :)
তিতাস

4

সি ++, 27243 অক্ষর গ্রিড (248x219, 50.2% ভরাট)

(এটিকে একটি নতুন উত্তর হিসাবে পোস্ট করা কারণ আমি প্রথমে রেফারেন্স হিসাবে পোস্ট করা 1D আবদ্ধ রাখতে চাই)

এই স্পষ্টতই ছড়িয়ে পড়েছে এটির মূল কাঠামোটিতে @ অ্যান্ডারস ক্যাসরগের উত্তরটি দিয়ে ভারী অনুপ্রাণিত হয়েছে, তবে কয়েকটি টুইট রয়েছে। প্রথমত, আমি সর্বোত্তম উপলভ্য ওভারল্যাপটি কেবল 3 টি অক্ষর না হওয়া পর্যন্ত স্ট্রিংগুলিকে মার্জ করার জন্য আমি আমার আসল প্রোগ্রামটি ব্যবহার করি। তারপরে আমি এই উত্পন্ন স্ট্রিংগুলি ব্যবহার করে অ্যান্ডারস ক্যাসরগের পদ্ধতিটি ক্রমান্বয়ে 2D গ্রিড পূরণ করার জন্য ব্যবহার করি। সীমাবদ্ধতাগুলিও কিছুটা আলাদা: এটি এখনও প্রতিবার সংক্ষিপ্ত অক্ষর যুক্ত করার চেষ্টা করে তবে প্রথমে স্কোয়ার গ্রিড, তারপরে ছোট ছোট গ্রিডের পক্ষে এবং শেষ পর্যন্ত দীর্ঘতম শব্দ যুক্ত করে বন্ধনগুলি ভেঙে দেওয়া হয়।

এটি যে আচরণটি প্রদর্শন করে তা হ'ল স্থান পূরণ এবং দ্রুত গ্রিড সম্প্রসারণের সময়কালের মধ্যে বিকল্প (দুর্ভাগ্যক্রমে এটি দ্রুত বর্ধনের পর্যায়ে শব্দের বাইরে চলে যায়, তাই প্রান্তের চারপাশে অনেক ফাঁকা জায়গা রয়েছে)। আমি ব্যয় ক্রিয়াকলাপের কিছুটা টুইট করে সন্দেহ করি যে এটি 50% এর চেয়ে বেশি স্থান পূরণ করা যায়।

এখানে 2 টি এক্সিকিউটেবল রয়েছে (অ্যালগরিদমকে পুনরাবৃত্তভাবে উন্নতি করার সময় পুরো প্রক্রিয়াটি পুনরায় চালনার প্রয়োজন এড়াতে)। একটির আউটপুট সরাসরি অন্যটিতে পাইপ করা যায়:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>

std::size_t calcOverlap(const std::string &a, const std::string &b, std::size_t limit, std::size_t minimal) {
    std::size_t la = a.size();
    for(std::size_t p = std::min(std::min(la, b.size()), limit + 1); -- p > minimal; ) {
        if(a.compare(la - p, p, b, 0, p) == 0) {
            return p;
        }
    }
    return 0;
}

bool isSameReversed(const std::string &a, const std::string &b) {
    std::size_t l = a.size();
    if(b.size() != l) {
        return false;
    }
    for(std::size_t i = 0; i < l; ++ i) {
        if(a[i] != b[l-i-1]) {
            return false;
        }
    }
    return true;
}

int main(int argc, const char *const *argv) {
    // Usage: prog [<stop_threshold>]

    std::size_t stopThreshold = 3;

    if(argc >= 2) {
        char *check;
        long v = std::strtol(argv[1], &check, 10);
        if(check == argv[1] || v < 0) {
            std::cerr
                << "Invalid stop threshold. Should be an integer >= 0"
                << std::endl;
            return 1;
        }
        stopThreshold = v;
    }

    std::vector<std::string> words;

    // Load all words from input and their reverses (words can be backwards now)
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(word);
        std::reverse(word.begin(), word.end());
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // Remove all fully subsumed words

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming checks: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest (not necessary but doesn't hurt. Makes finding maxlen a tiny bit easier)
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t maxlen = words.front().size();

    // Repeatedly combine most-compatible words until we reach the threshold
    std::size_t bestPossible = maxlen - 1;
    while(words.size() > 2) {
        auto bestA = words.begin();
        auto bestB = -- words.end();
        std::size_t bestOverlap = 0;
        for(auto p = ++ words.begin(), e = words.end(); p != e; ++ p) {
            if(p->size() - 1 <= bestOverlap) {
                continue;
            }
            for(auto q = words.begin(); q != p; ++ q) {
                std::size_t overlap = calcOverlap(*p, *q, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = p;
                    bestB = q;
                    bestOverlap = overlap;
                }
                overlap = calcOverlap(*q, *p, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = q;
                    bestB = p;
                    bestOverlap = overlap;
                }
            }
            if(bestOverlap == bestPossible) {
                break;
            }
        }
        if(bestOverlap <= stopThreshold) {
            break;
        }
        std::string newStr = std::move(*bestA);
        newStr.append(*bestB, bestOverlap, std::string::npos);

        if(bestA == -- words.end()) {
            words.pop_back();
            *bestB = std::move(words.back());
            words.pop_back();
        } else {
            *bestB = std::move(words.back());
            words.pop_back();
            *bestA = std::move(words.back());
            words.pop_back();
        }

        // Remove any words which are now in the result (forward or reverse)
        // (would not be necessary if we didn't have the reversed forms too)
        std::string newRev = newStr;
        std::reverse(newRev.begin(), newRev.end());
        for(auto p = words.begin(); p != words.end(); ) {
            if(newStr.find(*p) != std::string::npos || newRev.find(*p) != std::string::npos) {
                std::cerr << "Now subsumes: " << *p << std::endl;
                p = words.erase(p);
            } else {
                ++ p;
            }
        }

        std::cerr
            << "Words remaining: " << (words.size() + 1)
            << " Latest combination: (" << bestOverlap << ") " << newStr
            << std::endl;

        words.push_back(std::move(newStr));
        words.push_back(std::move(newRev));
        bestPossible = bestOverlap; // Merging existing words will never make longer merges possible
    }

    std::cerr
        << "After merging: " << words.size()
        << std::endl;

    // Remove all fully subsumed words (i.e. reversed words)

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        std::string rev = *p;
        std::reverse(rev.begin(), rev.end());
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos || i->find(rev) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest for display
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t len = 0;
    for(const auto &word : words) {
        std::cout
            << word
            << std::endl;
        len += word.size();
    }
    std::cerr
        << "Total size: " << len
        << std::endl;
    return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <limits>

class vec2 {
public:
    int x;
    int y;

    vec2(void) : x(0), y(0) {};
    vec2(int x, int y) : x(x), y(y) {}

    bool operator ==(const vec2 &b) const {
        return x == b.x && y == b.y;
    }

    vec2 &operator +=(const vec2 &b) {
        x += b.x;
        y += b.y;
        return *this;
    }

    vec2 &operator -=(const vec2 &b) {
        x -= b.x;
        y -= b.y;
        return *this;
    }

    vec2 operator +(const vec2 b) const {
        return vec2(x + b.x, y + b.y);
    }

    vec2 operator *(const int b) const {
        return vec2(x * b, y * b);
    }
};

class box2 {
public:
    vec2 tl;
    vec2 br;

    box2(void) : tl(), br() {};
    box2(vec2 a, vec2 b)
        : tl(std::min(a.x, b.x), std::min(a.y, b.y))
        , br(std::max(a.x, b.x) + 1, std::max(a.y, b.y) + 1)
    {}

    void grow(const box2 &b) {
        if(b.tl.x < tl.x) {
            tl.x = b.tl.x;
        }
        if(b.br.x > br.x) {
            br.x = b.br.x;
        }
        if(b.tl.y < tl.y) {
            tl.y = b.tl.y;
        }
        if(b.br.y > br.y) {
            br.y = b.br.y;
        }
    }

    bool intersects(const box2 &b) const {
        return (
            ((tl.x >= b.br.x) != (br.x > b.tl.x)) &&
            ((tl.y >= b.br.y) != (br.y > b.tl.y))
        );
    }

    box2 &operator +=(const vec2 b) {
        tl += b;
        br += b;
        return *this;
    }

    int width(void) const {
        return br.x - tl.x;
    }

    int height(void) const {
        return br.y - tl.y;
    }

    int maxdim(void) const {
        return std::max(width(), height());
    }
};

template <> struct std::hash<vec2> {
    std::size_t operator ()(const vec2 &o) const {
        return std::hash<int>()(o.x) + std::hash<int>()(o.y) * 997;
    }
};

template <class A,class B> struct std::hash<std::pair<A,B>> {
    std::size_t operator ()(const std::pair<A,B> &o) const {
        return std::hash<A>()(o.first) + std::hash<B>()(o.second) * 31;
    }
};

class word_placement {
public:
    vec2 start;
    vec2 dir;
    box2 bounds;
    const std::string *word;

    word_placement(vec2 start, vec2 dir, const std::string *word)
        : start(start)
        , dir(dir)
        , bounds(start, start + dir * (word->size() - 1))
        , word(word)
    {}

    word_placement(vec2 start, const word_placement &copy)
        : start(copy.start + start)
        , dir(copy.dir)
        , bounds(copy.bounds)
        , word(copy.word)
    {
        bounds += start;
    }

    word_placement(const word_placement &copy)
        : start(copy.start)
        , dir(copy.dir)
        , bounds(copy.bounds)
        , word(copy.word)
    {}
};

class word_placement_links {
public:
    std::unordered_set<word_placement*> placements;
    std::unordered_set<std::pair<char,word_placement*>> relativePlacements;
};

class grid {
public:
    std::vector<std::string> wordCache; // Just a block of memory for our pointers to reference
    std::unordered_map<vec2,char> state;
    std::unordered_set<word_placement*> placements;
    std::unordered_map<const std::string*,word_placement_links> wordPlacements;
    std::unordered_map<char,std::unordered_set<word_placement*>> relativeWordPlacements;
    box2 bound;

    grid(const std::vector<std::string> &words) {
        wordCache = words;
        std::vector<vec2> directions;
        directions.emplace_back(+1,  0);
        directions.emplace_back(+1, +1);
        directions.emplace_back( 0, +1);
        directions.emplace_back(-1, +1);
        directions.emplace_back(-1,  0);
        directions.emplace_back(-1, -1);
        directions.emplace_back( 0, -1);
        directions.emplace_back(+1, -1);

        wordPlacements.reserve(wordCache.size());
        placements.reserve(wordCache.size());
        relativeWordPlacements.reserve(64);

        std::size_t total = 0;
        for(const std::string &word : wordCache) {
            word_placement_links &p = wordPlacements[&word];
            p.placements.reserve(8);
            auto &rp = p.relativePlacements;
            std::size_t l = word.size();
            rp.reserve(l * directions.size());
            for(int i = 0; i < l; ++ i) {
                for(const vec2 &d : directions) {
                    word_placement *rwp = new word_placement(d * -i, d, &word);
                    rp.emplace(word[i], rwp);
                    relativeWordPlacements[word[i]].insert(rwp);
                }
            }
            total += l;
        }
        state.reserve(total);
    }

    const std::string *find_word(const std::string &word) const {
        for(const std::string &w : wordCache) {
            if(w == word) {
                return &w;
            }
        }
        throw std::string("Failed to find word in cache");
    }

    void remove_word(const std::string *word) {
        const word_placement_links &links = wordPlacements[word];
        for(word_placement *p : links.placements) {
            placements.erase(p);
            delete p;
        }
        for(auto &p : links.relativePlacements) {
            relativeWordPlacements[p.first].erase(p.second);
            delete p.second;
        }
        wordPlacements.erase(word);
    }

    void remove_placement(word_placement *placement) {
        wordPlacements[placement->word].placements.erase(placement);
        placements.erase(placement);
        delete placement;
    }

    bool check_placement(const word_placement &placement) const {
        vec2 p = placement.start;
        for(const char c : *placement.word) {
            auto i = state.find(p);
            if(i != state.end() && i->second != c) {
                return false;
            }
            p += placement.dir;
        }
        return true;
    }

    int check_new(const word_placement &placement) const {
        int n = 0;
        vec2 p = placement.start;
        for(const char c : *placement.word) {
            n += !state.count(p);
            p += placement.dir;
        }
        return n;
    }

    void check_placements(const box2 &b) {
        for(auto i = placements.begin(); i != placements.end(); ) {
            if(!b.intersects((*i)->bounds) || check_placement(**i)) {
                ++ i;
            } else {
                i = placements.erase(i);
            }
        }
    }

    void add_placement(const vec2 p, const word_placement &relative) {
        word_placement check(p, relative);
        if(check_placement(check)) {
            word_placement *wp = new word_placement(check);
            placements.insert(wp);
            wordPlacements[relative.word].placements.insert(wp);
        }
    }

    void place(word_placement placement) {
        remove_word(placement.word);
        int overlap = 0;
        for(const char c : *placement.word) {
            char &g = state[placement.start];
            if(g == '\0') {
                g = c;
                for(const word_placement *rp : relativeWordPlacements[c]) {
                    add_placement(placement.start, *rp);
                }
            } else if(g != c) {
                throw std::string("New word changes an existing character!");
            } else {
                ++ overlap;
            }
            placement.start += placement.dir;
        }
        bound.grow(placement.bounds);
        check_placements(placement.bounds);

        std::cerr
            << draw('.', "\n")
            << "Added " << *placement.word << " (overlap: " << overlap << ")"
            << ", Grid: " << bound.width() << "x" << bound.height() << " of " << state.size() << " chars"
            << ", Words remaining: " << wordPlacements.size()
            << std::endl;
    }

    int check_cost(box2 b) const {
        b.grow(bound);
        return (
            ((b.maxdim() - bound.maxdim()) << 16) |
            (b.width() + b.height() - bound.width() - bound.height())
        );
    }

    void add_next(void) {
        int bestNew = std::numeric_limits<int>::max();
        int bestCost = std::numeric_limits<int>::max();
        int bestLen = 0;
        word_placement *best = nullptr;
        for(word_placement *p : placements) {
            int n = check_new(*p);
            if(n <= bestNew) {
                int l = p->word->size();
                int cost = check_cost(box2(p->start, p->start + p->dir * l));
                if(n < bestNew || cost < bestCost || (cost == bestCost && l < bestLen)) {
                    bestNew = n;
                    bestCost = cost;
                    bestLen = l;
                    best = p;
                }
            }
        }
        if(best == nullptr) {
            throw std::string("Failed to find join to existing blob");
        }
        place(*best);
    }

    void fill(void) {
        while(!placements.empty()) {
            add_next();
        }
    }

    std::string draw(char blank, const std::string &linesep) const {
        std::string result;
        result.reserve((bound.width() + linesep.size()) * bound.height());
        for(int y = bound.tl.y; y < bound.br.y; ++ y) {
            for(int x = bound.tl.x; x < bound.br.x; ++ x) {
                auto c = state.find(vec2(x, y));
                result.push_back((c == state.end()) ? blank : c->second);
            }
            result.append(linesep);
        }
        return result;
    }

    box2 bounds(void) const {
        return bound;
    }

    int chars(void) const {
        return state.size();
    }
};

int main(int argc, const char *const *argv) {
    std::vector<std::string> words;

    // Load all words from input
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // initialise grid
    grid g(words);

    // add first word (order of input file means this is longest word)
    g.place(word_placement(vec2(0, 0), vec2(1, 0), g.find_word(words.front())));

    // add all other words
    g.fill();

    std::cout << g.draw('.', "\n");

    int w = g.bounds().width();
    int h = g.bounds().height();
    int n = g.chars();
    std::cerr
        << "Final grid: " << w << "x" << h
        << " with " << n << " characters"
        << " (" << (n * 100.0 / (w * h)) << "% filled)"
        << std::endl;
    return 0;
}

এবং অবশেষে, ফলাফল:

ফাইনাল গ্রিড


বিকল্প ফলাফল (নির্দিষ্ট কিছু দিকনির্দেশনা দিয়েছিল এবং ব্যয়টি ফাংশনটি টিকিয়ে রেখেছিল এমন প্রোগ্রামে বেশ কয়েকটি বাগ ফিক্স করার পরে, আমি আরও কমপ্যাক্ট তবে কম অনুকূল সমাধান পেয়েছি): 29275 অক্ষর, 198x195 (75.8% ভরা):

স্কোয়ার গ্রিড

আবার আমি এই প্রোগ্রামগুলি অপ্টিমাইজ করার জন্য খুব বেশি কিছু করি নি, তাই এটি কিছুটা সময় নেয়। এটি গ্রিডে ভরাট হওয়ার সাথে সাথে আপনি দেখতে পাবেন যা বেশ সম্মোহক।


2

সি ++, 34191 অক্ষর "গ্রিড" (ন্যূনতম মানুষের হস্তক্ষেপের সাথে, 6 বা 7 সহজেই সংরক্ষণ করা যায়)

এটি 2D কেসের জন্য আবদ্ধ হিসাবে আরও গ্রহণ করা উচিত, কারণ উত্তরটি এখনও 1D স্ট্রিং। এটি পূর্ববর্তী চ্যালেঞ্জ থেকে কেবল আমার কোড, তবে কোনও স্ট্রিং বিপরীত করার নতুন ক্ষমতা সহ। এটি আমাদেরকে শব্দের সংমিশ্রনের জন্য আরও অনেক সুযোগ দেয় (বিশেষত কারণ এটি নন-ওভারল্যাপিং সুপার স্ট্রিংয়ের সবচেয়ে খারাপ পরিস্থিতিটি ২ 26 এ বর্ণিত; বর্ণমালার প্রতিটি অক্ষরের জন্য একটি)।

কিছুটা সামান্য 2D ভিজ্যুয়াল আপিলের জন্য, এটি যদি নিখরচায় (যেমন 0-ওভারল্যাপ শব্দের মধ্যে) করতে পারে তবে ফলাফলটিতে লাইন ব্রেকগুলি রাখে।

খুব ধীর (এখনও কোনও ক্যাশে নেই)। কোডটি এখানে:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::size_t calcOverlap(const std::string &a, const std::string &b, std::size_t limit, std::size_t minimal) {
    std::size_t la = a.size();
    for(std::size_t p = std::min(std::min(la, b.size()), limit + 1); -- p > minimal; ) {
        if(a.compare(la - p, p, b, 0, p) == 0) {
            return p;
        }
    }
    return 0;
}

bool isSameReversed(const std::string &a, const std::string &b) {
    std::size_t l = a.size();
    if(b.size() != l) {
        return false;
    }
    for(std::size_t i = 0; i < l; ++ i) {
        if(a[i] != b[l-i-1]) {
            return false;
        }
    }
    return true;
}

int main() {
    std::vector<std::string> words;

    // Load all words from input and their reverses (words can be backwards now)
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(word);
        std::reverse(word.begin(), word.end());
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // Remove all fully subsumed words

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming checks: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest (not necessary but doesn't hurt. Makes finding maxlen a tiny bit easier)
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t maxlen = words.front().size();

    // Repeatedly combine most-compatible words until we have only 1 word left (+ its reverse)
    std::size_t bestPossible = maxlen - 1;
    while(words.size() > 2) {
        auto bestA = words.begin();
        auto bestB = -- words.end();
        std::size_t bestOverlap = 0;
        for(auto p = ++ words.begin(), e = words.end(); p != e; ++ p) {
            if(p->size() - 1 <= bestOverlap) {
                continue;
            }
            for(auto q = words.begin(); q != p; ++ q) {
                std::size_t overlap = calcOverlap(*p, *q, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = p;
                    bestB = q;
                    bestOverlap = overlap;
                }
                overlap = calcOverlap(*q, *p, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = q;
                    bestB = p;
                    bestOverlap = overlap;
                }
            }
            if(bestOverlap == bestPossible) {
                break;
            }
        }
        std::string newStr = std::move(*bestA);
        if(bestOverlap == 0) {
            newStr.push_back('\n');
        }
        newStr.append(*bestB, bestOverlap, std::string::npos);

        if(bestA == -- words.end()) {
            words.pop_back();
            *bestB = std::move(words.back());
            words.pop_back();
        } else {
            *bestB = std::move(words.back());
            words.pop_back();
            *bestA = std::move(words.back());
            words.pop_back();
        }

        // Remove any words which are now in the result (forward or reverse)
        // (would not be necessary if we didn't have the reversed forms too)
        std::string newRev = newStr;
        std::reverse(newRev.begin(), newRev.end());
        for(auto p = words.begin(); p != words.end(); ) {
            if(newStr.find(*p) != std::string::npos || newRev.find(*p) != std::string::npos) {
                std::cerr << "Now subsumes: " << *p << std::endl;
                p = words.erase(p);
            } else {
                ++ p;
            }
        }

        std::cerr
            << "Words remaining: " << (words.size() + 1)
            << " Latest combination: (" << bestOverlap << ") " << newStr
            << std::endl;

        words.push_back(std::move(newStr));
        words.push_back(std::move(newRev));
        bestPossible = bestOverlap; // Merging existing words will never make longer merges possible
    }

    std::cerr
        << "After non-trivial merging: " << words.size()
        << std::endl;

    if(words.size() == 2 && !isSameReversed(words.front(), words.back())) {
        // must be 2 palindromes, so just join them
        words.front().append(words.back());
    }

    std::string result = words.front();

    std::cout
        << result
        << std::endl;
    std::cerr
        << "Word size: " << result.size() // Note this number includes newlines, so to get the grid size according to the rules, subtract newlines manually
        << std::endl;
    return 0;
}

ফলাফল: http://pastebin.com/UTe2WMcz (আগের চ্যালেঞ্জের তুলনায় 4081 টি অক্ষর)

এটি অত্যন্ত স্পষ্ট যে দানব রেখাটি ছেদ করে xdএবং wvলাইনগুলি উল্লম্বভাবে রেখে কিছু তুচ্ছ बचत করা যায় । তারপরে এবং এর সাথে hhidetautisbneuduiছেদ করতে পারেdlxwwwowaxocnnaesddaw । এটি 4 টি অক্ষর সংরক্ষণ করে। অন্য 2 টি সংরক্ষণ করার nbcllilhnজন্য একটি বিদ্যমান sওভারল্যাপে (যদি একটি খুঁজে পাওয়া যায়) এ প্রতিস্থাপন করা যেতে পারে (বা কেবল 1 যদি এ জাতীয় কোনও ওভারল্যাপ বিদ্যমান না থাকে এবং পরিবর্তে এটি উল্লম্বভাবে যুক্ত করা উচিত)। পরিশেষেmjjrajaytq 1 টি সংরক্ষণ করার জন্য উল্লম্বভাবে কোথাও যুক্ত করা যেতে পারে এর অর্থ ন্যূনতম মানুষের হস্তক্ষেপের সাথে, ফলাফল থেকে 6-7 টি অক্ষর সংরক্ষণ করা যায়।

আমি নিম্নলিখিত পদ্ধতির সাথে এটি 2 ডি তে পেতে চাই, তবে আমি অ্যালগরিদম ও (এন ^ 4) না তৈরি করে এটি বাস্তবায়নের কোনও উপায় খুঁজে পেতে লড়াই করছি, যা গণনা করা বেশ অবৈধ!

  1. উপরের মতো অ্যালগরিদম চালান, তবে ওভারল্যাপগুলি 1 টি অক্ষরে পৌঁছে গেলে সংক্ষেপে থামুন
  2. পুনঃপুনঃ:
    1. 4 টি শব্দের একটি গোষ্ঠী সন্ধান করুন যা একটি আয়তক্ষেত্রের মধ্যে সাজানো যেতে পারে
    2. এই আয়তক্ষেত্রের উপরে যতটা সম্ভব শব্দ যুক্ত করুন যেখানে প্রতিটি শব্দ বর্তমান আকারের কমপক্ষে 2 টি অক্ষরকে ওভারল্যাপ করে (সমস্ত 8 টি দিক যাচাই করে দেখুন) - এটিই কেবলমাত্র এক পর্যায়ে যেখানে আমরা বর্তমান কোডের চেয়ে একটি সুবিধা পেতে পারি
  3. প্রতিটি সময় একক-বর্ণের জন্য ওভারল্যাপের সন্ধানকারী ফলাফলযুক্ত গ্রিড এবং একাকী শব্দের সংমিশ্রণ করুন

0

পিএইচপি

এই এক কাজ theroratically না; তবে 10000 সম্ভবত পুনরাবৃত্তির জন্য খুব বেশি শব্দ। স্ক্রিপ্ট এখন চলছে। (এখনও 24 ঘন্টা পরে চালানো)
ছোট ডিরেক্টরিতে সূক্ষ্ম কাজ করে তবে আমি পরের সপ্তাহে একটি পুনরাবৃত্ত সংস্করণ তৈরি করতে পারি।

$f=array("pen","op","po","ne","pro","aaa","abcd","dcba"); will output ABCD Apen arop Ao .. although this is not an optimal result (scoring was changed ... I´m working on a generator). One optimal result is this: খোলা .ra .oa dcba`

এটি খুব দ্রুত নয়; কেবলমাত্র সাবস্ট্রিংগুলি সরিয়ে দেয় এবং দৈর্ঘ্য অনুসারে অবশিষ্টাংশগুলি সাজায়,
বাকীটি হ'ল শক্তিশালী: শব্দগুলিকে একটি আয়তক্ষেত্রের সাথে ফিট করার চেষ্টা করুন, ব্যর্থ হলে আরও বড় আয়তক্ষেত্রের চেষ্টা করুন।

বিটিডব্লিউ: বৃহত ডিরেক্টরিটির জন্য সাবস্ট্রিং অংশটির জন্য আমার মেশিনে 4.5 মিনিটের প্রয়োজন হয়
এবং এটি 6,190 শব্দে কেটে যায়; তাদের উপর বাছাই 11 সেকেন্ড সময় নেয়।

$f=file('https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english.txt');
// A: remove substrings - forward or reversed
$s=join(' ',$f);
$haystack="$s ".strrev($s);
foreach($f as$w)
{
    $r=strrev($w=trim($w)); // remove trailing line break and create reverse word
    if(!preg_match("%$w\w|\w$w%",$haystack)
        // no substr match ... now: is the reverse word in the list?
        // if so, keep only the lower one (ascii values)
        &!($w>$r&&strstr($s,$r))
        // strstr does NOT render the reverse substr regex obsolete:
        // this is only executed for $w=abc, not for $w=bca!
    )
        $g[]=$w
    ;
}

// B: sort the words by length
usort($g,function($a,$b){return strlen($a)-strlen($b);});

// C1: function to fit $words into $map
function gomap($words,$map)
{
    $h=count($map);$w=strlen($map[0]);
    $len=strlen($word=array_pop($words));
    // $x,$y=position; $d=0:horizontal, $d=1:vertical; $r=0: word, $r=1: reverse word
    for($x=$w-$len;$x>=0;$x--)for($y=$h-$len;$y>=0;$y--)for($d=0;$d<2;$d++)for($r=0;$r<2;$r++)
    {
        // does the word fit there?
        $drow=$r?strrev($word):$word;
        for($ok=1,$i=0;$ok&$i<$len;$i++)
            $ok=in_array($map[$y+$d*$i][$x+$i-$d*$i], [' ',$drow[$i]])
        ;
        // it does, paint it
        if($ok)
        {
            for($i=0;$i<$len;$i++)
                $map[$y+$d*$i][$x+$i-$d*$i]=$drow[$i];
            if(!count($words))      // this was the last word: return map
                return $map;
            else                    // there are more words: recurse
                if ($ok=gomap($words,$map))
                    return $ok;
            // no fit, try next position
        }
    }
    return 0;
}

// C2: rectangle loop
for($h=0;++$h;)for($w=0;$w++<$h;)   // define a rectangle
{
    // and try to fit the words in there
    if($map=gomap($g,
        array_fill(0,$h,str_repeat(' ',$w))
    ))
    {
        // words fit; output and break loops
        echo '<pre>',implode("\n",$map),'</pre>';
        break 2;
    }
}

যখন প্রোগ্রামটি একটি ছোট অভিধানে চালিত হয় তখন আপনি একটি উদাহরণ অন্তর্ভুক্ত করতে পারেন?
লুভজো

আমি আসলে স্কোরিং বদলেছি (দুঃখিত!)। অব্যক্ত অক্ষরের সংখ্যাটি আপনার স্কোরটিতে অন্তর্ভুক্ত নয়।
নাথান মেরিল

2
এখানে লুপিংয়ের অর্থ এটি ~ O ((w * h) h n)। আমরা জানি সমাধানটিতে 35 কে অক্ষরের মতো কিছু থাকবে (শেষ চ্যালেঞ্জ থেকে), সুতরাং এটি প্রায় 35000 ^ 6000 বার গোম্যাপে কল করবে। আমার ক্যালকুলেটর আমাকে বলে যে এটি "অনন্ত"। আরও ভাল ক্যালকুলেটর আমাকে আসল নম্বরটি বলেছে ( wolframalpha.com/input/?i=35000%5E6000 )। এখন, আমরা যদি ধরে নিই যে মহাবিশ্বের প্রতিটি পরমাণু এই প্রোগ্রামটি পরিচালনার জন্য নিবেদিত একটি 3 টেরাহার্টজ প্রসেসর, মহাবিশ্বটি এটি সম্পন্ন হওয়ার আগে এখনও 10 ^ 27154 গুণ বেশি সময়ের জন্য বিদ্যমান থাকতে হবে for আমি যা বলছি তা হ'ল: এটি শেষ হওয়ার জন্য অপেক্ষা করবেন না!
ডেভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.